e Please enter the commit message for your changes. Lines starting

remove node modules
:wq
This commit is contained in:
dmacias72 2016-11-06 11:17:39 -07:00
parent e30bf5242f
commit 16846d0db8
10047 changed files with 16 additions and 1161991 deletions

View File

@ -84,11 +84,14 @@ else
upgradepkg --install-new &plgPATH;/&plgNAME;.txz upgradepkg --install-new &plgPATH;/&plgNAME;.txz
npm -g update
npm -g install ungit
#restart event daemon #restart event daemon
<![CDATA[ <![CDATA[
/usr/local/emhttp/plugins/ungit/scripts/stop >/dev/null 2>&1 < /dev/null & /usr/local/emhttp/plugins/ungit/scripts/stop >/dev/null 2>&1 < /dev/null &
sleep 1 sleep 1
echo "starting libvirtwol..." echo "starting ungit..."
setsid /usr/local/emhttp/plugins/ungit/scripts/start >/dev/null 2>&1 < /dev/null & setsid /usr/local/emhttp/plugins/ungit/scripts/start >/dev/null 2>&1 < /dev/null &
]]> ]]>

View File

@ -1,8 +1,7 @@
#!/bin/sh #!/bin/sh
# start/stop/restart ungit daemon: # start/stop/restart ungit daemon:
PLG="ungit" PLG="ungit"
EMHTTP="/usr/local/emhttp/plugins/$PLG" PROG="/usr/lib64/node_modules/$PLG/src/server.js"
PROG="$EMHTTP/node_modules/$PLG/src/server.js"
LOCKFILE="/var/lock/$PLG" LOCKFILE="/var/lock/$PLG"
PIDFILE="/var/run/$PLG.pid" PIDFILE="/var/run/$PLG.pid"
CONFIG="/boot/config/plugins/$PLG/$PLG.cfg" CONFIG="/boot/config/plugins/$PLG/$PLG.cfg"
@ -17,7 +16,6 @@ ungit_start() {
if [ "$DAEMON" == "enable" ]; then if [ "$DAEMON" == "enable" ]; then
echo "starting $PLG..." echo "starting $PLG..."
sleep 1 sleep 1
#cd $EMHTTP/node_modules/$PLG
nohup /usr/bin/node $PROG --port="$PORT" --logDirectory="/var/log/" --logGitCommands --logGitOutput >/var/log/$PLG 2>&1 | echo $! > $PIDFILE & nohup /usr/bin/node $PROG --port="$PORT" --logDirectory="/var/log/" --logGitCommands --logGitOutput >/var/log/$PLG 2>&1 | echo $! > $PIDFILE &
touch $LOCKFILE touch $LOCKFILE
TIMER=0 TIMER=0
@ -58,6 +56,14 @@ ungit_restart() {
ungit_start ungit_start
} }
# Restart ungit:
ungit_update() {
ungit_stop
sleep 1
npm -g update
ungit_start
}
case "$1" in case "$1" in
'start') 'start')
ungit_start ungit_start
@ -68,6 +74,9 @@ case "$1" in
'restart') 'restart')
ungit_restart ungit_restart
;; ;;
'update')
ungit_update
;;
*) *)
echo "usage rc.ungit: start|stop|restart" echo "usage rc.ungit: start|stop|restart"
esac esac

View File

@ -1 +0,0 @@
../ungit/bin/credentials-helper

View File

@ -1 +0,0 @@
../ungit/bin/ungit

View File

@ -1,3 +0,0 @@
* text=auto
/bin/credentials-helper eol=lf
/bin/ungit eol=lf

View File

@ -1,3 +0,0 @@
/public/*
.git/*
.git

View File

@ -1,40 +0,0 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
node_modules/
.ungitrc
.travis.yml
Gruntfile.js
assets/
test/
report/
public/source/
public/vendor/css/
public/vendor/js/
public/templates/
public/devStyling.html
public/js/devStyling.js
clicktests/
clicktestout/
teststabilitytester.js
build/
*.dll
nw.exe
nw.pak

View File

@ -1,52 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/) and
[Keep a changlelog's changelog standard](http://keepachangelog.com/)
## [Unreleased](https://github.com/FredrikNoren/ungit/compare/v0.10.3...master)
## [0.10.3](https://github.com/FredrikNoren/ungit/compare/v0.10.2...v0.10.3)
### Fixed
- Missing npm as a normal dependency [#766] (https://github.com/FredrikNoren/ungit/issues/766)
## [0.10.2](https://github.com/FredrikNoren/ungit/compare/v0.10.1...v0.10.2)
### Added
- Added bare repo support [#177](https://github.com/FredrikNoren/ungit/issues/177) [#728](https://github.com/FredrikNoren/ungit/issues/728)
- Added support for cherry-pick conflict[#701](https://github.com/FredrikNoren/ungit/issues/701)
- Added wordwrap support for diffs [#721](https://github.com/FredrikNoren/ungit/issues/721)
- Support for Node6 [#745](https://github.com/FredrikNoren/ungit/pull/745/files)
- Added "autoCheckoutOnBranchCreate" option [#752](https://github.com/FredrikNoren/ungit/pull/752/files)
### Fixed
- Fix maxConcurrentGitOperations not limiting git processes [#707](https://github.com/FredrikNoren/ungit/issues/707)
- Fix ".lock" file conflicts in parallelized git operations [#515](https://github.com/FredrikNoren/ungit/issues/515)
- Allow Ungit to function under sub dir of a git dir [#734](https://github.com/FredrikNoren/ungit/issues/734)
- Removed deprecated npmconf package [#746](https://github.com/FredrikNoren/ungit/issues/746)
- More helpful warning messages [#749](https://github.com/FredrikNoren/ungit/pull/749/files)
- Deleting already deleted remote tag [#748](https://github.com/FredrikNoren/ungit/pull/748)
- Fix to handle revert merge commit [#757](https://github.com/FredrikNoren/ungit/pull/757)
### Changed
- Cleaner rebase conflict message display [#708](https://github.com/FredrikNoren/ungit/pull/708)
- ES6 [#672](https://github.com/FredrikNoren/ungit/pull/672)
- Dropped support for Node 0.10 and 0.12 [#745](https://github.com/FredrikNoren/ungit/pull/745/files)
## [0.10.1](https://github.com/FredrikNoren/ungit/compare/v0.10.0...v0.10.1)
### Added
- Introduced change log! [#687](https://github.com/FredrikNoren/ungit/issues/687)
- Improved server and client error logging [#695](https://github.com/FredrikNoren/ungit/pull/695)
### Fixed
- Fix crashes due to submodule parsing [#690](https://github.com/FredrikNoren/ungit/issues/690) [#689](https://github.com/FredrikNoren/ungit/issues/689)
- Fix duplicate remote tag issues [#685](https://github.com/FredrikNoren/ungit/issues/685)
- Fix scrolling issue in safari [#686](https://github.com/FredrikNoren/ungit/issues/686)
- Fix git hooks failing on non-ascii files [#676](https://github.com/FredrikNoren/ungit/issues/676)
### Removed
- Reverted on hover button effects [#688](https://github.com/FredrikNoren/ungit/issues/688)
### Changed
- Upgrade keen.io client code [#679](https://github.com/FredrikNoren/ungit/issues/679)

View File

@ -1,88 +0,0 @@
These are the contributing guidelines as well as some documentation on how the code is structured. Read up before contributing to make everything as smooth as possible.
Posting issues
==============
Just common sense; do a quick search before posting, someone might already have created an issue (or resolved the problem!). If you're posting a bug; try to include as much relevant information as possible (ungit version, node and npm version, os, any git errors displayed, output from cli console and output from the browser console).
Pull requests
=============
Make sure to include a note in CHANGELOG.md about the change as part of the PR.
We follow the ["Keep a changelog"](http://keepachangelog.com/) style guide.
Make sure to include reference's to issues and PR's when relevant in the change log.
Writing plugins
===============
See [PLUGINS.md](PLUGINS.md)
Developing for Ungit proper
===========================
I do accept pull requests, but I also reserve the right to not do so for things I don't think fit with Ungit. If you are developing anything new you should almost always also provide tests for it, preferably clicktests but it doesn't hurt to write REST-interface tests as well if applicable. Try to post pull requests early, even at a concept stage, to get feedback and increase chances it's merged.
What you need to get started
----------------------------
You'll need the same as for Ungit; node, npm and git. You will also need grunt (`npm install -g grunt-cli`).
Getting started
---------------
To get started developing on Ungit:
1. Make sure you have [node.js](http://nodejs.org), [npm](https://npmjs.org/), [git](http://git-scm.com/) and [grunt](http://gruntjs.com/) installed.
2. Clone the repository to a local directory.
3. Run `npm install` to install dependencies.
4. Run `grunt` to build (compile templates, css and js).
5. Type `npm start` to start ungit, or `npm test` to run tests.
6. (Optional). Run `grunt watch` to automatically rebuild stuff when you change files.
Run ungit as standalone application
-----------------------------------
To provide easier access to launch ungit, very early stage of standalone application container using [electron](http://electron.atom.io/) is available.
Please note this is not yet ready for public release and being developed having several known & unknown limitations.
To get started:
1. Follow steps in 'Getting started' to get a development environment ready.
2. Run `grunt default && grunt package`. This will compile latest ungit and will create a standalone application package under `build/`
Known limitation:
1. Current standalone application does not allow to execute more than one instance.
2. There is no installer package neither automatic update mechanism for standalone application in place yet.
Additional notes:
1. To create windows package with proper application description on non-windows platform, [wine](https://www.winehq.org/) is required to be installed. If not, windows package will be created with default resources.
Architecture overview
---------------------
Ungit has two major parts; the server and the ui. The server exposes a REST interfaces, which enables it to be run on a remote server. The UI is a single-page web-app, built using Knockout.js.
Folders
-------
* `assets/` Raw assets used for development.
* `bin/` "Binary" files, the ungit launcher script and the credentials-helper, which is invoked by git to acquire credentials when using http authentication.
* `clicktests/` Phantom.js clicktest; basically tests that run on the rendered DOM. Since these run all the way, from the DOM down to the server, they're also the most powerful of the tests.
* `components/` This directory contains all view components for Ungit, each of them exposed as an Ungit plugin.
* `public/` The UI web-app.
* `public/css/` CSS generated by the grunt script.
* `public/fonts/` & `public/images` Assets, some of which are compiled into the CSS by the grunt script, others (for instance those that are too large to compile into the CSS efficiently) are served directly.
* `public/js/` An ungit.js file generated by the grunt script, as well as raven files which handle exception logging.
* `public/less/` Less files, which are the "source" used to generate the CSS.
* `public/source/` Javascript source code, which is turned into the public/js/ungit.js file by the grunt script.
* `public/vendor/` Various 3rd-party libs.
* `source/` Server and shared (i.e. used by both server and UI) source code.
* `test/` Unit tests and REST interface tests.
Running tests
-------------
`grunt test` will run both unit tests, REST-interface tests, and clicktests. `grunt unittest` only runs the tests in the test/ folder, `grunt clicktest` runs only the tests in the clicktests/ folder. Install mocha (`npm install -g mocha`) to run specific tests in the test folder and get better stack traces: `mocha test/spec.git-api.js`.
Things to consider when developing
----------------------------------
* Try to make everything as touch friendly as possible, for instance no mouse over tooltips (try to make it clear without that). Everything doesn't adhere 100% to that right now but I'm trying to move it more in that direction.
* Write tests. The most important tests to write are usually the clicktests since they will cover the most code (both ui and backend).

View File

@ -1,76 +0,0 @@
Writing Ungit plugins
=====================
It's super easy to write an Ungit plugin. Here's how to write a completely new (though super simple) git log ui:
### 1. Create a new folder for your plugin.
Create a folder at `~/.ungit/plugins/MY_FANCY_PLUGIN`, then add a file called `ungit-plugin.json` with the following content:
```JSON
{
"exports": {
"javascript": "example.js"
}
}
```
### 2. Add some code
Create an `example.js` file and add this:
```JavaScript
var components = require('ungit-components');
// We're overriding the graph component here
components.register('graph', function(args) {
return {
// This method creates and returns the DOM node that represents this component.
updateNode: function() {
var node = document.createElement('div');
// Request all log entries from the backend
args.server.get('/log', { path: args.repoPath, limit: 50 }, function(err, log) {
// Add all log entries to the parent node
log.forEach(function(entry) {
var entryNode = document.createElement('div');
entryNode.innerHTML = entry.message;
node.appendChild(entryNode);
});
});
return node;
}
};
});
```
### 3. Done!
Just restart Ungit, or if you have `"dev": true` in your `.ungitrc` you can just refresh your browser. A [gerrit plugin example](https://github.com/FredrikNoren/ungit-gerrit) can be found here.
### Ungit Plugin API version
The Ungit Plugin API follows semver, and the current version can be found in the package.json (ungitPluginApiVersion). On the frontend it can be accessed from `ungit.pluginApiVersion` and on the backend `env.pluginApiVersion`.
### Components
Each functionalities within ungit is built as components. Each components is an ungit plugin that is checked into main repository. All the components in Ungit is built as plugins, take a look in the [components](https://github.com/FredrikNoren/ungit/tree/master/components) directory for inspiration.
An [example](https://github.com/FredrikNoren/ungit/tree/master/components/staging) of ungit component with view can be seen below.
```JSON
{
"exports": {
"knockoutTemplates": {
"staging": "staging.html"
},
"javascript": "staging.bundle.js",
"css": "staging.css"
}
}
```
* Views(html) for Component
Each component can have multiple views as exampled [here](https://github.com/FredrikNoren/ungit/tree/master/components/dialogs).
* CSS for Component
css file can be easily defined per components and in above example we can see that `staging.less` file is compiled into `staging.css` via grunt job. If you are using less file please modify [Gruntfile.js](https://github.com/FredrikNoren/ungit/blob/master/Gruntfile.js) file to include new less file.
* JS for Component
Each component gets to have one javascipt files. However each javasciprt file can require other javascript in it's directory or other libraries. If you are doing require by relative pass as exampled in [graph.js](https://github.com/FredrikNoren/ungit/blob/master/components/graph/graph.js), you wouldn't have to include the js in browserify job in [Gruntfile.js](https://github.com/FredrikNoren/ungit/blob/master/Gruntfile.js).

View File

@ -1,103 +0,0 @@
ungit
======
[![NPM version](https://badge.fury.io/js/ungit.svg)](http://badge.fury.io/js/ungit)
[![Build Status](https://travis-ci.org/FredrikNoren/ungit.svg)](https://travis-ci.org/FredrikNoren/ungit)
[![Join the chat at https://gitter.im/FredrikNoren/ungit](https://badges.gitter.im/FredrikNoren/ungit.svg)](https://gitter.im/FredrikNoren/ungit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
The easiest way to use git. On any platform. Anywhere.
[![xkcd](xkcd.png)](https://xkcd.com/1597/)
Git is known for being a versatile distributed source control system that is a staple of many individuals, communities, and even for [the City of Chattanooga to crowd source bicycle parking locations](https://github.com/cityofchattanooga/Bicycle-Parking). However, it is not known for userfriendlyness or easy learning curve.
Ungit is to bring user friendliness to git without sacrificing versatility of git.
* Clean and intuitive UI that makes it easy to _understand_ git.
* Runs on any platform that node.js & git supports.
* Web-based, meaning you can run it on your cloud/pure shell machine and use the ui from your browser (just browse to http://your-cloud-machine.com:8448).
* Works well with GitHub.
* [Gerrit](https://code.google.com/p/gerrit/) integration through plugin: https://github.com/FredrikNoren/ungit-gerrit
[Follow @ungitui on twitter](https://twitter.com/ungitui)
Quick intro to ungit: [http://youtu.be/hkBVAi3oKvo](http://youtu.be/hkBVAi3oKvo)
[![Screenshot](screenshot.png)](http://youtu.be/hkBVAi3oKvo)
Installing
----------
Requires [node.js](http://nodejs.org) (≥ 0.10), [npm](https://www.npmjs.com/) (≥ 1.3.1, comes with node.js) and [git](http://git-scm.com/) (≥ 1.8.x). To install ungit just type:
npm install -g ungit
NOTE: If your system requires root access to install global npm packages, make sure you use the -H flag:
sudo -H npm install -g ungit
Using
-----
Anywhere you want to start, just type:
ungit
This will launch the server and open up a browser with the ui.
Configuring
---------
Put a configuration file called .ungitrc in your home directory (`/home/USERNAME` on *nix, `C:/Users/USERNAME/` on windows). Can be in either json or ini format. See [source/config.js](source/config.js) for available options.
You can also override configuration variables at launch by specifying them as command line arguments; `ungit --port=8080`. To disable boolean features use --no: `ungit --no-autoFetch`.
Example of `~/.ungitrc` configuration file to change default port and enable bugtracking:
```json
{
"port": 8080,
"bugtracking": true
}
```
Ungit uses [rc](https://github.com/dominictarr/rc) for configuration, which in turn uses [yargs](https://github.com/yargs/yargs) for command line arguments. See corresponding documentations for more details.
Plugins
-------
Plugins are installed by simply placing them in the Ungit plugin directory (`~/.ungit/plugins` by default), and then restarting Ungit.
[List of available plugins](https://github.com/FredrikNoren/ungit/wiki/List-of-plugins)
There's a guide in the [PLUGINS.md](PLUGINS.md) file on how to write new plugins.
Developing
----------
See [CONTRIBUTING.md](CONTRIBUTING.md).
Maintainers
-----------
* [FredrikNoren](https://github.com/FredrikNoren)
* [codingtwinky](https://github.com/codingtwinky)
Known issues
------------
* If you're running MacOSX Mavericks and Ungit crashes after a few seconds; try updating npm and node. See [#259](https://github.com/FredrikNoren/ungit/issues/259) and [#249](https://github.com/FredrikNoren/ungit/issues/249) for details.
* Ubuntu users may have trouble installing because the node executable is named differently on Ubuntu, see [#401](https://github.com/FredrikNoren/ungit/issues/401) for details.
* Debian Wheezy's supported git and nodejs packages are too old, therefore download newest [git](https://github.com/git/git/releases) and [nodejs](https://nodejs.org/download/) tarballs and [build from source](http://www.control-escape.com/linux/lx-swinstall-tar.html).
Changelog
---------
See [CHANGELOG.md](CHANGELOG.md).
License (MIT)
-------------
Copyright (C) 2013-2016 Fredrik Norén
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[![Dependency Status](https://david-dm.org/FredrikNoren/ungit.png)](https://david-dm.org/FredrikNoren/ungit)
[![devDependency Status](https://david-dm.org/FredrikNoren/ungit/dev-status.png)](https://david-dm.org/FredrikNoren/ungit#info=devDependencies)

View File

@ -1,18 +0,0 @@
environment:
matrix:
- nodejs_version: "6.0"
install:
- ps: Install-Product node $env:nodejs_version
- git config --global user.email "test@testy.com"
- git config --global user.name "Test testy"
- npm install
- npm install -g grunt-cli
test_script:
- node --version
- npm --version
- grunt
- npm test
build: off

View File

@ -1,46 +0,0 @@
#!/usr/bin/env node
var config = require('../src/config');
var winston = require('winston');
var path = require('path');
var async = require('async');
winston.remove(winston.transports.Console);
var BugTracker = require('../src/bugtracker');
var bugtracker = new BugTracker('credentials-helper');
var usageStatistics = require('../src/usage-statistics');
process.on('uncaughtException', function(err) {
console.error(err.stack.toString());
async.parallel([
bugtracker.notify.bind(bugtracker, err, 'credentials-helper'),
usageStatistics.addEvent.bind(usageStatistics, 'credentials-helper-exception')
], function() {
process.exit();
});
});
if (config.logDirectory)
winston.add(winston.transports.File, { filename: path.join(config.logDirectory, 'credentials-helper.log'), maxsize: 100*1024, maxFiles: 2 });
var socketId = process.argv[2];
var port = process.argv[3];
winston.info('Credentials helper invoked; port ' + port + ', socketId ' + socketId);
var http = require('http');
if (process.argv[4] == 'get') {
winston.info('Getting credentials');
http.get('http://localhost:' + port + '/api/credentials?socketId=' + socketId, function(res) {
winston.info('Got credentials');
res.on('data', function(body) {
var data = JSON.parse(body);
console.log('username=' + data.username);
console.log('password=' + (data.password ? data.password : ''));
});
}).on('error', function(err) {
winston.error('Error getting credentials, couldn\'t query server', err);
});
} else {
winston.info('Unhandled param: ' + process.argv[4]);
}

View File

@ -1,98 +0,0 @@
#!/usr/bin/env node
var startLaunchTime = Date.now();
var forever = require('forever-monitor');
var config = require('../src/config');
var open = require('open');
var path = require('path');
var child_process = require('child_process');
var async = require('async');
var BugTracker = require('../src/bugtracker');
var bugtracker = new BugTracker('launcher');
var usageStatistics = require('../src/usage-statistics');
process.on('uncaughtException', function(err) {
console.error(err.stack.toString());
async.parallel([
bugtracker.notify.bind(bugtracker, err, 'ungit-launcher'),
usageStatistics.addEvent.bind(usageStatistics, 'launcher-exception')
], function() {
process.exit();
});
});
var child = new (forever.Monitor)(path.join(__dirname, '..', 'src', 'server.js'), {
silent: false,
minUptime: 2000,
max: config.maxNAutoRestartOnCrash,
cwd: path.join(process.cwd(), '..'),
options: process.argv.slice(2),
env: { LANG: 'en_US.UTF-8' }
});
child.on('exit', function (res) {
console.log('Stopped keeping ungit alive');
});
function launch(callback) {
var currentUrl = config.urlBase + ':' + config.port + config.rootPath;
if (config.forcedLaunchPath === undefined) currentUrl += '/#/repository?path=' + encodeURIComponent(process.cwd());
else if (config.forcedLaunchPath !== null && config.forcedLaunchPath !== '') currentUrl += '/#/repository?path=' + encodeURIComponent(config.forcedLaunchPath);
console.log('Browse to ' + currentUrl);
if (config.launchBrowser && !config.launchCommand) {
open(currentUrl);
} else if (config.launchCommand) {
var command = config.launchCommand.replace(/%U/g, currentUrl);
console.log('Running custom launch command: ' + command);
child_process.exec(command, function(err, stdout, stderr) {
if (err) {
callback(err);
return;
}
if (config.launchBrowser)
open(currentUrl);
});
}
}
function startupListener(data) {
if (data.toString().indexOf('## Ungit started ##') >= 0) {
launch(function(err) {
if (err) console.log(err);
});
child.removeListener('stdout', startupListener);
var launchTime = (Date.now() - startLaunchTime);
console.log('Took ' + launchTime + 'ms to start server.');
usageStatistics.addEvent('server-start', { launchTimeMs: launchTime });
}
}
child.on('stdout', startupListener);
function checkIfUngitIsRunning(callback) {
// Fastest way to find out if a port is used or not/i.e. if ungit is running
var net = require('net');
var server = net.createServer(function(c) { });
server.listen(config.port, function(err) {
server.close(function() {
callback(null, false);
});
});
server.on('error', function (e) {
if (e.code == 'EADDRINUSE') {
callback(null, true);
}
});
}
checkIfUngitIsRunning(function(err1, ungitRunning) {
if (ungitRunning) {
console.log('Ungit server already running');
launch(function(err) {
if (err) console.log(err);
});
}
else {
child.start();
}
});

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
.app-wrapper {
padding-top: 21px;
}

View File

@ -1,61 +0,0 @@
<!-- ko component: header --><!-- /ko -->
<div class="app-top-margin"></div>
<div class="app-wrapper">
<div class="container" data-bind="shown: shown" data-ta-container="app">
<div class="alert alert-danger" data-bind="visible: gitVersionErrorVisible">
<span data-bind="text: gitVersionError"></span>
<button type="button" class="close" data-bind="click: dismissGitVersionError">&times;</button>
</div>
<div class="alert alert-info" data-bind="visible: newVersionAvailable">
A new version of ungit (<span data-bind="text: latestVersion"></span>) is <a href="https://github.com/FredrikNoren/ungit">available</a>! Run <code data-bind="text: newVersionInstallCommand"></code> to install. (You are currently running version <span data-bind="text: currentVersion"></span>.)
<button type="button" class="close" data-dismiss="alert">&times;</button>
</div>
<div class="alert alert-info clearfix" data-bind="visible: showBugtrackingNagscreen">
<button type="button" class="close" data-bind="click: dismissBugtrackingNagscreen">&times;</button>
<p><strong>Help make ungit better with the press of a button!</strong></p>
<button class="btn btn-primary" data-bind="click: enableBugtrackingAndStatistics">Enable automatic bug reports + anonymous usage statistics</button>
<button class="btn btn-primary" data-bind="click: enableBugtracking">Enable automatic bug reports</button>
<button class="btn btn-default" data-bind="click: dismissBugtrackingNagscreen">Naah, I&#39;ll skip that</button>
</div>
<div class="alert alert-info clearfix" data-bind="visible: showNPSSurvey">
<button type="button" class="close" data-bind="click: dismissNPSSurvey">&times;</button>
<span class="text-dimmed">Hi! This is a one-question survey to learn more about how people use Ungit. You can dismiss it by clicking the x in the upper right corner.</span>
<p><h4>Question: How likely are you to recommend Ungit to your friends and colleagues?</h4></p>
<p>
<div class="btn-group btn-group-justified">
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 0)">0</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 1)">1</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 2)">2</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 3)">3</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 4)">4</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 5)">5</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 6)">6</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 7)">7</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 8)">8</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 9)">9</a>
<a class="btn btn-default" role="button" data-bind="click: sendNPS.bind(null, 10)">10</a>
</div>
<div class="clearfix">
Not at all likely
<div class="pull-right">Extremely likely</div>
</div>
</p>
</div>
</div>
<!-- ko if: content -->
<div class="container container-wide" data-bind="component: content"></div>
<!-- /ko -->
</div>
<!-- ko if: dialog -->
<!-- ko template: { name: templateChooser, data: dialog } --><!-- /ko -->
<!-- /ko -->

View File

@ -1,166 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
var navigation = require('ungit-navigation');
components.register('app', function(args) {
return new AppViewModel(args.appContainer, args.server);
});
var AppViewModel = function(appContainer, server) {
var self = this;
this.appContainer = appContainer;
this.server = server;
if (window.location.search.indexOf('noheader=true') < 0)
this.header = components.create('header', { app: this });
this.dialog = ko.observable(null);
this.repoList = ko.observableArray(JSON.parse(localStorage.getItem('repositories') || localStorage.getItem('visitedRepositories') || '[]')); // visitedRepositories is legacy, remove in the next version
this.repoList.subscribe(function(newValue) { localStorage.setItem('repositories', JSON.stringify(newValue)); });
this.content = ko.observable(components.create('home', { app: this }));
this.currentVersion = ko.observable();
this.latestVersion = ko.observable();
this.newVersionAvailable = ko.observable();
this.newVersionInstallCommand = (ungit.platform == 'win32' ? '' : 'sudo -H ') + 'npm update -g ungit';
this.bugtrackingEnabled = ko.observable(ungit.config.bugtracking);
this.bugtrackingNagscreenDismissed = ko.observable(localStorage.getItem('bugtrackingNagscreenDismissed'));
this.showBugtrackingNagscreen = ko.computed(function() {
return !self.bugtrackingEnabled() && !self.bugtrackingNagscreenDismissed();
});
this.gitVersionErrorDismissed = ko.observable(localStorage.getItem('gitVersionErrorDismissed'));
this.gitVersionError = ko.observable();
this.gitVersionErrorVisible = ko.computed(function() {
return !ungit.config.gitVersionCheckOverride && self.gitVersionError() && !self.gitVersionErrorDismissed();
});
var NPSSurveyLastDismissed = parseInt(localStorage.getItem('NPSSurveyLastDismissed') || '0');
var monthsSinceNPSLastDismissed = (Date.now() - NPSSurveyLastDismissed) / (1000 * 60 * 60 * 24 * 30);
this.showNPSSurvey = ko.observable(monthsSinceNPSLastDismissed >= 6 && Math.random() < 0.01);
this.sendNPS = function(value) {
keen.addEvent('survey-nps', {
version: ungit.version,
userHash: ungit.userHash,
rating: value,
bugtrackingEnabled: ungit.config.bugtracking,
sendUsageStatistics: ungit.config.sendUsageStatistics
});
self.dismissNPSSurvey();
}
}
AppViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('app', this, {}, parentElement);
}
AppViewModel.prototype.template = 'app';
AppViewModel.prototype.shown = function() {
var self = this;
// The ungit.config variable collections configuration from all different paths and only updates when
// ungit is restarted
if(!ungit.config.bugtracking) {
// Whereas the userconfig only reflects what's in the ~/.ungitrc and updates directly,
// but is only used for changing around the configuration. We need to check this here
// since ungit may have crashed without the server crashing since we enabled bugtracking,
// and we don't want to show the nagscreen twice in that case.
this.server.get('/userconfig', undefined, function(err, userConfig) {
self.bugtrackingEnabled(userConfig.bugtracking);
});
}
this.server.get('/latestversion', undefined, function(err, version) {
if (!version) return;
self.currentVersion(version.currentVersion);
self.latestVersion(version.latestVersion);
self.newVersionAvailable(version.outdated);
});
this.server.get('/gitversion', undefined, function(err, gitversion) {
if (!gitversion) return;
if (!gitversion.satisfied) {
self.gitVersionError(gitversion.error);
}
});
}
AppViewModel.prototype.updateAnimationFrame = function(deltaT) {
if (this.content() && this.content().updateAnimationFrame) this.content().updateAnimationFrame(deltaT);
}
AppViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'request-credentials') this._handleCredentialsRequested(event);
else if (event.event == 'request-show-dialog') this.showDialog(event.dialog);
else if (event.event == 'request-remember-repo') this._handleRequestRememberRepo(event);
if (this.content() && this.content().onProgramEvent)
this.content().onProgramEvent(event);
if (this.header && this.header.onProgramEvent) this.header.onProgramEvent(event);
}
AppViewModel.prototype._handleRequestRememberRepo = function(event) {
var repoPath = event.repoPath;
if (this.repoList.indexOf(repoPath) != -1) return;
this.repoList.push(repoPath);
}
AppViewModel.prototype._handleCredentialsRequested = function() {
var self = this;
var diag;
// Only show one credentials dialog if we're asked to show another one while the first one is open
// This happens for instance when we fetch nodes and remote tags at the same time
if (this._isShowingCredentialsDialog)
diag = self.dialog();
else {
diag = components.create('credentialsdialog');
self.showDialog(diag);
}
this._isShowingCredentialsDialog = true;
diag.closed.add(function() {
self._isShowingCredentialsDialog = false;
programEvents.dispatch({ event: 'request-credentials-response', username: diag.username(), password: diag.password() });
});
}
AppViewModel.prototype.showDialog = function(dialog) {
var self = this;
dialog.closed.add(function() {
self.dialog(null);
})
this.dialog(dialog);
}
AppViewModel.prototype.enableBugtrackingAndStatistics = function() {
var self = this;
this.server.get('/userconfig', undefined, function(err, userConfig) {
if (err) return;
userConfig.bugtracking = true;
userConfig.sendUsageStatistics = true;
self.server.post('/userconfig', userConfig, function(err) {
if (err) return;
self.bugtrackingEnabled(true);
});
});
}
AppViewModel.prototype.enableBugtracking = function() {
var self = this;
this.server.get('/userconfig', undefined, function(err, userConfig) {
if (err) return;
userConfig.bugtracking = true;
self.server.post('/userconfig', userConfig, function(err) {
if (err) return;
self.bugtrackingEnabled(true);
});
});
}
AppViewModel.prototype.dismissBugtrackingNagscreen = function() {
localStorage.setItem('bugtrackingNagscreenDismissed', true);
this.bugtrackingNagscreenDismissed(true);
}
AppViewModel.prototype.dismissGitVersionError = function() {
localStorage.setItem('gitVersionErrorDismissed', true);
this.gitVersionErrorDismissed(true);
}
AppViewModel.prototype.dismissNPSSurvey = function() {
this.showNPSSurvey(false);
localStorage.setItem('NPSSurveyLastDismissed', Date.now());
}
AppViewModel.prototype.templateChooser = function(data) {
if (!data) return '';
return data.template;
};

View File

@ -1,8 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"app": "app.html"
},
"javascript": "app.bundle.js"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,18 +0,0 @@
<div class="btn-group">
<button type="button" class="btn btn-default btn-main" data-ta-clickable="branch" data-bind="click: updateBranches">
<span class="octicon octicon-git-branch"></span>
<span data-bind="text: fetchLabel"></span>
<!-- ko component: fetchingProgressBar --><!-- /ko -->
</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-ta-clickable="branch-menu">
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu" data-ta-container="branches">
<!-- ko foreach: branches -->
<li>
<a href="#" data-bind="text: name, click: $parent.checkoutBranch.bind($parent), attr: { 'data-ta-clickable': 'checkout' + name }"></a>
<a href="#" class="list-link list-remove" data-bind="click: $parent.branchRemove.bind($parent), attr: { 'data-ta-clickable': name + '-remove' }">X</a>
</li>
<!-- /ko -->
</ul>
</div>

View File

@ -1,87 +0,0 @@
var ko = require('knockout');
var _ = require('lodash');
var async = require('async');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
components.register('branches', function(args) {
return new BranchesViewModel(args.server, args.repoPath);
});
function BranchesViewModel(server, repoPath) {
var self = this;
this.repoPath = repoPath;
this.server = server;
this.branches = ko.observableArray();
this.fetchingProgressBar = components.create('progressBar', { predictionMemoryKey: 'fetching-' + this.repoPath(), temporary: true });
this.current = ko.observable();
this.fetchLabel = ko.computed(function() {
if (self.current()) {
return self.current();
}
});
this.updateBranches();
}
BranchesViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('branches', this, {}, parentElement);
}
BranchesViewModel.prototype.clickFetch = function() { this.updateBranches(); }
BranchesViewModel.prototype.onProgramEvent = function(event) {
if (event.event === 'working-tree-changed' || event.event == 'request-app-content-refresh' || event.event == 'branch-updated') {
this.updateBranches();
}
}
BranchesViewModel.prototype.checkoutBranch = function(branch) {
var self = this;
this.fetchingProgressBar.start();
this.server.post('/checkout', { path: this.repoPath(), name: branch.name }, function(err) {
if (err) return;
self.current(branch.name);
self.fetchingProgressBar.stop();
});
}
BranchesViewModel.prototype.updateBranches = function() {
var self = this;
this.fetchingProgressBar.start();
this.server.get('/branches', { path: this.repoPath() }, function(err, branches) {
if (err) {
self.current("~error");
return;
}
if (branches) {
var sorted = branches.sort(function(a, b) {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
});
self.branches(sorted);
self.current(undefined);
branches.forEach(function(branch) {
if (branch.current) {
self.current(branch.name);
}
});
}
self.fetchingProgressBar.stop();
});
}
BranchesViewModel.prototype.branchRemove = function(branch) {
var self = this;
var diag = components.create('yesnodialog', { title: 'Are you sure?', details: 'Deleting ' + branch.name + ' branch cannot be undone with ungit.'});
diag.closed.add(function() {
if (diag.result()) {
self.server.del('/branches', { name: branch.name, path: self.repoPath() }, function(err) {
if (!err) {
programEvents.dispatch({ event: 'working-tree-changed' });
}
});
}
});
programEvents.dispatch({ event: 'request-show-dialog', dialog: diag });
}

View File

@ -1,8 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"branches": "branches.html"
},
"javascript": "branches.bundle.js"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,92 +0,0 @@
.commit {
position: relative;
}
.commit.highlighted {
z-index: 2;
}
.commit.highlighted .commit-box {
box-shadow: 5px 5px 0px rgba(0, 0, 0, 0.2);
background: #4B5766;
left: -5px;
}
.commit.highlighted .commit-box .arrowRight .shadow {
display: block;
}
.commit.highlighted .commit-box .arrowRight .arrow {
border-left: 15px solid #4A5665;
}
.commit.hover {
z-index: 3;
}
.commit.selected .details .diff-wrapper {
transition: width 0.1s, left 0.05s;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.15);
}
.commit.selected .details .diff-wrapper .diff-inner {
box-shadow: 5px 5px 0px rgba(0, 0, 0, 0.2);
padding: 10px;
padding-top: 0px;
}
.commit.selected .details .diff-wrapper .btn-group {
padding: 2px;
margin: 10px 0px 0px 10px;
margin-bottom: 0px;
}
.commit .commit-box .arrowRight {
position: absolute;
right: 0px;
top: 5px;
}
.commit .commit-box .arrowRight .shadow {
display: none;
height: 0px;
width: 0px;
margin-left: -1px;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #1E2029;
}
.commit .commit-box .arrowRight .arrow {
height: 0px;
width: 0px;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #3b4552;
}
.commit .commit-box > .panel-body {
position: relative;
padding: 10px;
margin-bottom: 0px;
width: 400px;
min-height: 85px;
}
.commit .commit-box > .panel-body .gravatar {
display: none;
margin-right: 10px;
}
.commit .commit-box > .panel-body .title {
font-size: 1.3em;
word-wrap: break-word;
display: block;
}
.commit .commit-box > .panel-body .details .body {
white-space: pre-wrap;
word-wrap: break-word;
color: #8F9FA6;
}
.commit .commit-box > .panel-body .details .diff-wrapper {
position: relative;
margin: 0px;
margin-top: 10px;
margin-bottom: 10px;
background: #4B5766;
border-radius: 3px;
}
@media (min-width: 992px) {
.commit .commit-box > .panel-body {
width: 550px;
}
.commit .commit-box > .panel-body .gravatar {
display: block;
}
}

View File

@ -1,52 +0,0 @@
<div class="commit" data-bind="css: { highlighted: highlighted, hover: nodeIsMousehover, selected: selected }">
<div class="commit-box panel panel-default" data-bind="element: element, click: stopClickPropagation">
<div class="panel-body">
<div class="arrowContainer arrowRight">
<div class="shadow"></div>
<div class="arrow"></div>
</div>
<div class="clearfix">
<img class="pull-left img-circle gravatar"
data-bind="attr: { src: 'http://www.gravatar.com/avatar/' + authorGravatar() + '?default=404' }"
onerror="this.style.display='none';">
<div>
<div>
<span class="title" data-bind="text: title"></span>
<span class="text-muted">by <a data-bind="text: authorName, attr: { href: 'mailto:' + authorEmail() }"></a></span>
</div>
<div class="text-muted nodeSummaryContainer">
<span data-bind="text: authorDateFromNow, attr: { 'data-original-title': authorDate }" class="bootstrap-tooltip" data-toggle="tooltip" data-placement="bottom" data-delay='{"show":"2000", "hide":"0"}'></span> |
+<span data-bind="text: numberOfAddedLines"></span>,
-<span data-bind="text: numberOfRemovedLines"></span>
|
<span data-bind="text: sha1.substr(0, 8)"></span>
</div>
</div>
</div>
<!-- ko if: selected() || nodeIsMousehover() -->
<div class="details">
<div class="body" data-bind="text: body"></div>
<div class="diff-wrapper" data-bind="visible: showCommitDiff, style: diffStyle, click: stopClickPropagation">
<div class="btn-group btn-group-xs" data-bind="visible: selected">
<button class="btn btn-default bootstrap-tooltip pull-right" data-ta-clickable="commit-wordwrap" data-bind="click: toggleWordWrap.bind($data, true), css: {active: wordWrap}" data-toggle="tooltip" data-placement="bottom" data-original-title="Wrap words per line" data-delay='{"show":"2000", "hide":"0"}'>
<span data-bind="text: 'Word Wrap'"></span>
</button>
<button class="btn btn-default bootstrap-tooltip pull-right" data-ta-clickable="commit-nowrap" data-bind="click: toggleWordWrap.bind($data, false), css: {active: !wordWrap()}" data-toggle="tooltip" data-placement="bottom" data-original-title="Not wrapping words per line" data-delay='{"show":"2000", "hide":"0"}'>
<span data-bind="text: 'Default'"></span>
</button>
</div>
<div class="btn-group btn-group-xs" data-bind="visible: selected">
<button class="btn btn-default bootstrap-tooltip pull-right" data-ta-clickable="commit-sideBySideDiff" data-bind="click: textDiffTypeChange.bind($data, 'sidebysidediff'), css: {active: textDiffType() === 'sidebysidediff'}" data-toggle="tooltip" data-placement="bottom" data-original-title="Show side by side diff view" data-delay='{"show":"2000", "hide":"0"}'>
<span data-bind="text: 'Side by Side'"></span>
</button>
<button class="btn btn-default bootstrap-tooltip pull-right" data-ta-clickable="commit-defaultDiff" data-bind="click: textDiffTypeChange.bind($data, 'textdiff'), css: {active: textDiffType() === 'textdiff'}" data-toggle="tooltip" data-placement="bottom" data-original-title="Show inline diff view" data-delay='{"show":"2000", "hide":"0"}'>
<span data-bind="text: 'Default'"></span>
</button>
</div>
<div class="diff-inner" data-bind="component: commitDiff"></div>
</div>
</div>
<!-- /ko -->
</div>
</div>
</div>

View File

@ -1,93 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var navigation = require('ungit-navigation');
var programEvents = require('ungit-program-events');
var md5 = require('blueimp-md5');
var moment = require('moment');
components.register('commit', function(args) {
return new CommitViewModel(args);
});
function CommitViewModel(args) {
var self = this;
this.repoPath = args.repoPath;
this.sha1 = args.sha1;
this.server = args.server;
this.highlighted = args.highlighted;
this.nodeIsMousehover = args.nodeIsMousehover;
this.selected = args.selected;
this.element = ko.observable();
this.commitTime = ko.observable();
this.authorTime = ko.observable();
this.message = ko.observable();
this.title = ko.observable();
this.body = ko.observable();
this.authorDate = ko.observable(0);
this.authorDateFromNow = ko.observable();
this.authorName = ko.observable();
this.authorEmail = ko.observable();
this.fileLineDiffs = ko.observable();
this.numberOfAddedLines = ko.observable();
this.numberOfRemovedLines = ko.observable();
this.authorGravatar = ko.computed(function() { return md5(self.authorEmail()); });
this.textDiffType = ko.observable('textdiff');
this.wordWrap = ko.observable(false);
this.showCommitDiff = ko.computed(function() {
return self.fileLineDiffs() && self.fileLineDiffs().length > 0;
});
this.selectedDiffLeftPosition = ko.observable();
this.diffStyle = ko.computed(function() {
if (self.selected()) return { left: self.selectedDiffLeftPosition() + 'px', width: (window.innerWidth - 220) + 'px' };
else return { left: '0px', width: self.element() ? ((self.element().clientWidth - 20) + 'px') : 'inherit' };
});
}
CommitViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('commit', this, {}, parentElement);
}
CommitViewModel.prototype.setData = function(args) {
this.commitTime(moment(new Date(args.commitDate)));
this.authorTime(moment(new Date(args.authorDate)));
var message = args.message.split('\n');
this.message(args.message);
this.title(message[0]);
this.body(message.slice(2).join('\n'));
this.authorDate(moment(new Date(args.authorDate)));
this.authorDateFromNow(this.authorDate().fromNow());
this.authorName(args.authorName);
this.authorEmail(args.authorEmail);
this.numberOfAddedLines(args.fileLineDiffs.length > 0 ? args.fileLineDiffs[0][0] : 0);
this.numberOfRemovedLines(args.fileLineDiffs.length > 0 ? args.fileLineDiffs[0][1] : 0);
this.fileLineDiffs(args.fileLineDiffs);
this.isInited = true;
this.commitDiff = ko.observable(components.create('commitDiff',
{ fileLineDiffs: this.fileLineDiffs(),
sha1: this.sha1,
repoPath: this.repoPath,
server: this.server,
textDiffType: this.textDiffType,
wordWrap: this.wordWrap }));
}
CommitViewModel.prototype.updateLastAuthorDateFromNow = function(deltaT) {
this.lastUpdatedAuthorDateFromNow = this.lastUpdatedAuthorDateFromNow || 0;
this.lastUpdatedAuthorDateFromNow += deltaT;
if(this.lastUpdatedAuthorDateFromNow > 60 * 1000) {
this.lastUpdatedAuthorDateFromNow = 0;
this.authorDateFromNow(this.authorDate().fromNow());
}
}
CommitViewModel.prototype.updateAnimationFrame = function(deltaT) {
this.updateLastAuthorDateFromNow(deltaT);
}
CommitViewModel.prototype.textDiffTypeChange = function(type) {
this.textDiffType(type);
}
CommitViewModel.prototype.stopClickPropagation = function(data, event) {
event.stopImmediatePropagation();
}
CommitViewModel.prototype.toggleWordWrap = function(state) {
this.wordWrap(state);
}

View File

@ -1,114 +0,0 @@
@import "public/less/variables.less";
.commit {
position: relative;
&.highlighted {
z-index: 2;
.commit-box {
box-shadow: 5px 5px 0px rgba(0, 0, 0, 0.2);
background: #4B5766;
left: -5px;
.arrowRight {
.shadow {
display: block;
}
.arrow {
border-left: 15px solid #4A5665;
}
}
}
}
&.hover {
z-index: 3;
}
&.selected {
.details {
.diff-wrapper {
transition:width 0.1s, left 0.05s;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.15);
.diff-inner {
box-shadow: 5px 5px 0px rgba(0, 0, 0, 0.2);
padding: 10px;
padding-top: 0px;
}
.btn-group {
padding: 2px;
margin: 10px 0px 0px 10px;
margin-bottom: 0px;
}
}
}
}
.commit-box {
.arrowRight {
position: absolute;
right: 0px;
top: 5px;
.shadow {
display: none;
height: 0px;
width: 0px;
margin-left: -1px;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #1E2029;
}
.arrow {
height: 0px;
width: 0px;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 15px solid #3b4552;
}
}
}
.commit-box > .panel-body {
position: relative;
padding: 10px;
margin-bottom: 0px;
width: @log-width-small;
min-height: 85px;
.gravatar {
display: none;
margin-right: 10px;
}
.title {
font-size: 1.3em;
word-wrap: break-word;
display: block;
}
.details {
.body {
white-space: pre-wrap;
word-wrap: break-word;
color: #8F9FA6;
}
.diff-wrapper {
position: relative;
margin: 0px;
margin-top: 10px;
margin-bottom: 10px;
background: #4B5766;
border-radius: 3px;
}
}
}
}
@media (min-width: @screen-md-min) {
.commit {
.commit-box > .panel-body {
width: @log-width-large;
.gravatar {
display: block;
}
}
}
}

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"commit": "commit.html"
},
"javascript": "commit.bundle.js",
"css": "commit.css"
}
}

View File

@ -1,73 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var CommitLineDiff = require('./commitlinediff.js').CommitLineDiff;
var components = require('ungit-components');
components.register('commitDiff', function(args) {
return new CommitDiff(args);
});
var CommitDiff = function(args) {
this.commitLineDiffs = ko.observableArray();
this.sha1 = args.sha1;
args.fileLineDiffs.shift(); // remove first line that has "total"
this.loadFileLineDiffs(args);
};
CommitDiff.prototype.updateNode = function(parentElement) {
ko.renderTemplate('commitdiff', this, {}, parentElement);
};
CommitDiff.prototype.loadFileLineDiffs = function(args) {
var tempCommitLineDiffs = [];
var lineDiffLength = this.commitLineDiffs().length;
args.fileLineDiffs.slice(lineDiffLength === 0 ? 0 : lineDiffLength + 1, this.maxNumberOfFilesShown).forEach(function(fileLineDiff) {
tempCommitLineDiffs.push(new CommitLineDiff(args, fileLineDiff));
});
this.commitLineDiffs(this.commitLineDiffs().concat(tempCommitLineDiffs));
}
},{"./commitlinediff.js":2,"knockout":"knockout","ungit-components":"ungit-components"}],2:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
var inherits = require('util').inherits;
var programEvents = require('ungit-program-events');
var CommitLineDiff = function(args, fileLineDiff) {
this.added = ko.observable(fileLineDiff[0]);
this.removed = ko.observable(fileLineDiff[1]);
this.fileName = ko.observable(fileLineDiff[2]);
this.fileType = fileLineDiff[3];
this.isShowingDiffs = ko.observable(false);
this.repoPath = args.repoPath;
this.server = args.server;
this.sha1 = args.sha1;
this.textDiffType = args.textDiffType;
this.wordWrap = args.wordWrap;
this.specificDiff = ko.observable(this.getSpecificDiff());
};
exports.CommitLineDiff = CommitLineDiff;
CommitLineDiff.prototype.getSpecificDiff = function() {
return components.create(this.fileType + 'diff', {
filename: this.fileName(),
repoPath: this.repoPath,
server: this.server,
sha1: this.sha1,
textDiffType: this.textDiffType,
isShowingDiffs: this.isShowingDiffs,
wordWrap: this.wordWrap
});
}
CommitLineDiff.prototype.fileNameClick = function() {
this.isShowingDiffs(!this.isShowingDiffs());
this.specificDiff().invalidateDiff(function() {
programEvents.dispatch({ event: 'graph-render' });
});
};
},{"knockout":"knockout","ungit-components":"ungit-components","ungit-program-events":"ungit-program-events","util":undefined}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL2NvbW1pdGRpZmYvY29tbWl0ZGlmZi5qcyIsImNvbXBvbmVudHMvY29tbWl0ZGlmZi9jb21taXRsaW5lZGlmZi5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsInZhciBrbyA9IHJlcXVpcmUoJ2tub2Nrb3V0Jyk7XG52YXIgQ29tbWl0TGluZURpZmYgPSByZXF1aXJlKCcuL2NvbW1pdGxpbmVkaWZmLmpzJykuQ29tbWl0TGluZURpZmY7XG52YXIgY29tcG9uZW50cyA9IHJlcXVpcmUoJ3VuZ2l0LWNvbXBvbmVudHMnKTtcblxuY29tcG9uZW50cy5yZWdpc3RlcignY29tbWl0RGlmZicsIGZ1bmN0aW9uKGFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBDb21taXREaWZmKGFyZ3MpO1xufSk7XG5cbnZhciBDb21taXREaWZmID0gZnVuY3Rpb24oYXJncykge1xuICB0aGlzLmNvbW1pdExpbmVEaWZmcyA9IGtvLm9ic2VydmFibGVBcnJheSgpO1xuICB0aGlzLnNoYTEgPSBhcmdzLnNoYTE7XG4gIGFyZ3MuZmlsZUxpbmVEaWZmcy5zaGlmdCgpOyAgLy8gcmVtb3ZlIGZpcnN0IGxpbmUgdGhhdCBoYXMgXCJ0b3RhbFwiXG4gIHRoaXMubG9hZEZpbGVMaW5lRGlmZnMoYXJncyk7XG59O1xuXG5Db21taXREaWZmLnByb3RvdHlwZS51cGRhdGVOb2RlID0gZnVuY3Rpb24ocGFyZW50RWxlbWVudCkge1xuICBrby5yZW5kZXJUZW1wbGF0ZSgnY29tbWl0ZGlmZicsIHRoaXMsIHt9LCBwYXJlbnRFbGVtZW50KTtcbn07XG5cbkNvbW1pdERpZmYucHJvdG90eXBlLmxvYWRGaWxlTGluZURpZmZzID0gZnVuY3Rpb24oYXJncykge1xuICB2YXIgdGVtcENvbW1pdExpbmVEaWZmcyA9IFtdO1xuICB2YXIgbGluZURpZmZMZW5ndGggPSB0aGlzLmNvbW1pdExpbmVEaWZmcygpLmxlbmd0aDtcblxuICBhcmdzLmZpbGVMaW5lRGlmZnMuc2xpY2UobGluZURpZmZMZW5ndGggPT09IDAgPyAwIDogbGluZURpZmZMZW5ndGggKyAxLCB0aGlzLm1heE51bWJlck9mRmlsZXNTaG93bikuZm9yRWFjaChmdW5jdGlvbihmaWxlTGluZURpZmYpIHtcbiAgICB0ZW1wQ29tbWl0TGluZURpZmZzLnB1c2gobmV3IENvbW1pdExpbmVEaWZmKGFyZ3MsIGZpbGVMaW5lRGlmZikpO1xuICB9KTtcblxuICB0aGlzLmNvbW1pdExpbmVEaWZmcyh0aGlzLmNvbW1pdExpbmVEaWZmcygpLmNvbmNhdCh0ZW1wQ29tbWl0TGluZURpZmZzKSk7XG59XG4iLCJ2YXIga28gPSByZXF1aXJlKCdrbm9ja291dCcpO1xudmFyIGNvbXBvbmVudHMgPSByZXF1aXJlKCd1bmdpdC1jb21wb25lbnRzJyk7XG52YXIgaW5oZXJpdHMgPSByZXF1aXJlKCd1dGlsJykuaW5oZXJpdHM7XG52YXIgcHJvZ3JhbUV2ZW50cyA9IHJlcXVpcmUoJ3VuZ2l0LXByb2dyYW0tZXZlbnRzJyk7XG5cbnZhciBDb21taXRMaW5lRGlmZiA9IGZ1bmN0aW9uKGFyZ3MsIGZpbGVMaW5lRGlmZikge1xuICB0aGlzLmFkZGVkID0ga28ub2JzZXJ2YWJsZShmaWxlTGluZURpZmZbMF0pO1xuICB0aGlzLnJlbW92ZWQgPSBrby5vYnNlcnZhYmxlKGZpbGVMaW5lRGlmZlsxXSk7XG4gIHRoaXMuZmlsZU5hbWUgPSBrby5vYnNlcnZhYmxlKGZpbGVMaW5lRGlmZlsyXSk7XG4gIHRoaXMuZmlsZVR5cGUgPSBmaWxlTGluZURpZmZbM107XG4gIHRoaXMuaXNTaG93aW5nRGlmZnMgPSBrby5vYnNlcnZhYmxlKGZhbHNlKTtcbiAgdGhpcy5yZXBvUGF0aCA9IGFyZ3MucmVwb1BhdGg7XG4gIHRoaXMuc2VydmVyID0gYXJncy5zZXJ2ZXI7XG4gIHRoaXMuc2hhMSA9IGFyZ3Muc2hhMTtcbiAgdGhpcy50ZXh0RGlmZlR5cGUgPSBhcmdzLnRleHREaWZmVHlwZTtcbiAgdGhpcy53b3JkV3JhcCA9IGFyZ3Mud29yZFdyYXA7XG4gIHRoaXMuc3BlY2lmaWNEaWZmID0ga28ub2JzZXJ2YWJsZSh0aGlzLmdldFNwZWNpZmljRGlmZigpKTtcbn07XG5leHBvcnRzLkNvbW1pdExpbmVEaWZmID0gQ29tbWl0TGluZURpZmY7XG5cbkNvbW1pdExpbmVEaWZmLnByb3RvdHlwZS5nZXRTcGVjaWZpY0RpZmYgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIGNvbXBvbmVudHMuY3JlYXRlKHRoaXMuZmlsZVR5cGUgKyAnZGlmZicsIHtcbiAgICBmaWxlbmFtZTogdGhpcy5maWxlTmFtZSgpLFxuICAgIHJlcG9QYXRoOiB0aGlzLnJlcG9QYXRoLFxuICAgIHNlcnZlcjogdGhpcy5zZXJ2ZXIsXG4gICAgc2hhMTogdGhpcy5zaGExLFxuICAgIHRleHREaWZmVHlwZTogdGhpcy50ZXh0RGlmZlR5cGUsXG4gICAgaXNTaG93aW5nRGlmZnM6IHRoaXMuaXNTaG93aW5nRGlmZnMsXG4gICAgd29yZFdyYXA6IHRoaXMud29yZFdyYXBcbiAgfSk7XG59XG5cbkNvbW1pdExpbmVEaWZmLnByb3RvdHlwZS5maWxlTmFtZUNsaWNrID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuaXNTaG93aW5nRGlmZnMoIXRoaXMuaXNTaG93aW5nRGlmZnMoKSk7XG4gIHRoaXMuc3BlY2lmaWNEaWZmKCkuaW52YWxpZGF0ZURpZmYoZnVuY3Rpb24oKSB7XG4gICAgcHJvZ3JhbUV2ZW50cy5kaXNwYXRjaCh7IGV2ZW50OiAnZ3JhcGgtcmVuZGVyJyB9KTtcbiAgfSk7XG59O1xuIl19

View File

@ -1,39 +0,0 @@
.commitdiff {
width: 100%;
}
.commitdiff .file {
margin-top: 5px;
background: rgba(255, 255, 255, 0.16);
border-radius: 3px;
}
.commitdiff .file .head {
display: block;
cursor: pointer;
padding: 3px;
padding-left: 6px;
padding-right: 6px;
color: rgba(255, 255, 255, 0.79);
word-wrap: break-word;
}
.commitdiff .file .head .file-stats span:nth-of-type(1)::Before {
content: "+";
}
.commitdiff .file .head .file-stats span:nth-of-type(1) {
color: #9BF3A9;
}
.commitdiff .file .head .file-stats span:nth-of-type(2)::Before {
content: "-";
}
.commitdiff .file .head .file-stats span:nth-of-type(2) {
color: #EC9D93;
}
.commitdiff .file .diff {
background: rgba(0, 0, 0, 0.11);
}
.commitdiff .file .diff .textDiff {
color: rgba(255, 255, 255, 0.3);
}
.loadMore .btn {
display: block;
margin: 20px 20px 10px 20px;
}

View File

@ -1,14 +0,0 @@
<div data-bind="foreach: commitLineDiffs" class="commitdiff">
<div class="file">
<div class="head" data-bind="click: fileNameClick" data-ta-clickable="commitDiffFileName">
<span data-bind="text: fileName"></span>
<span class="file-stats" data-bind="visible: added() != '-'">
(
<span data-bind="text: added"></span>
<span data-bind="text: removed"></span>
)
</span>
</div>
<div class="diffContainer" data-bind="component: specificDiff" data-ta-container="commitLineDiffs"></div>
</div>
</div>

View File

@ -1,29 +0,0 @@
var ko = require('knockout');
var CommitLineDiff = require('./commitlinediff.js').CommitLineDiff;
var components = require('ungit-components');
components.register('commitDiff', function(args) {
return new CommitDiff(args);
});
var CommitDiff = function(args) {
this.commitLineDiffs = ko.observableArray();
this.sha1 = args.sha1;
args.fileLineDiffs.shift(); // remove first line that has "total"
this.loadFileLineDiffs(args);
};
CommitDiff.prototype.updateNode = function(parentElement) {
ko.renderTemplate('commitdiff', this, {}, parentElement);
};
CommitDiff.prototype.loadFileLineDiffs = function(args) {
var tempCommitLineDiffs = [];
var lineDiffLength = this.commitLineDiffs().length;
args.fileLineDiffs.slice(lineDiffLength === 0 ? 0 : lineDiffLength + 1, this.maxNumberOfFilesShown).forEach(function(fileLineDiff) {
tempCommitLineDiffs.push(new CommitLineDiff(args, fileLineDiff));
});
this.commitLineDiffs(this.commitLineDiffs().concat(tempCommitLineDiffs));
}

View File

@ -1,40 +0,0 @@
.commitdiff {
width: 100%;
.file {
margin-top: 5px;
background: rgba(255, 255, 255, 0.16);
border-radius: 3px;
.head {
display: block;
cursor: pointer;
padding: 3px;
padding-left: 6px;
padding-right: 6px;
color: rgba(255, 255, 255, 0.79);
word-wrap: break-word;
.file-stats {
span:nth-of-type(1)::Before{ content: "+" }
span:nth-of-type(1){
color: #9BF3A9;
}
span:nth-of-type(2)::Before{ content: "-" }
span:nth-of-type(2){
color: #EC9D93;
}
}
}
.diff {
background: rgba(0, 0, 0, 0.11);
.textDiff {
color: rgba(255, 255, 255, 0.3);
}
}
}
}
.loadMore {
.btn {
display: block;
margin: 20px 20px 10px 20px;
}
}

View File

@ -1,38 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var inherits = require('util').inherits;
var programEvents = require('ungit-program-events');
var CommitLineDiff = function(args, fileLineDiff) {
this.added = ko.observable(fileLineDiff[0]);
this.removed = ko.observable(fileLineDiff[1]);
this.fileName = ko.observable(fileLineDiff[2]);
this.fileType = fileLineDiff[3];
this.isShowingDiffs = ko.observable(false);
this.repoPath = args.repoPath;
this.server = args.server;
this.sha1 = args.sha1;
this.textDiffType = args.textDiffType;
this.wordWrap = args.wordWrap;
this.specificDiff = ko.observable(this.getSpecificDiff());
};
exports.CommitLineDiff = CommitLineDiff;
CommitLineDiff.prototype.getSpecificDiff = function() {
return components.create(this.fileType + 'diff', {
filename: this.fileName(),
repoPath: this.repoPath,
server: this.server,
sha1: this.sha1,
textDiffType: this.textDiffType,
isShowingDiffs: this.isShowingDiffs,
wordWrap: this.wordWrap
});
}
CommitLineDiff.prototype.fileNameClick = function() {
this.isShowingDiffs(!this.isShowingDiffs());
this.specificDiff().invalidateDiff(function() {
programEvents.dispatch({ event: 'graph-render' });
});
};

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"commitdiff": "commitdiff.html"
},
"javascript": "commitdiff.bundle.js",
"css": "commitdiff.css"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,149 +0,0 @@
var ko = require('knockout');
var signals = require('signals');
var inherits = require('util').inherits;
var components = require('ungit-components');
components.register('formdialog', function(args) {
return new FormDialogViewModel(args.title);
});
components.register('credentialsdialog', function(args) {
return new CredentialsDialogViewModel();
});
components.register('addremotedialog', function(args) {
return new AddRemoteDialogViewModel();
});
components.register('addsubmoduledialog', function(args) {
return new AddSubmoduleDialogViewModel();
});
components.register('promptdialog', function(args) {
return new PromptDialogViewModel(args.title, args.details);
});
components.register('yesnodialog', function(args) {
return new YesNoDialogViewModel(args.title, args.details);
});
components.register('yesnomutedialog', function(args) {
return new YesNoMuteDialogViewModel(args.title, args.details);
});
components.register('TooManyFilesDialogViewModel', function(args) {
return new TooManyFilesDialogViewModel(args.title, args.details);
});
function DialogViewModel(title) {
this.closed = new signals.Signal();
this.title = ko.observable(title);
this.taDialogName = ko.observable('');
}
DialogViewModel.prototype.setCloser = function(closer) {
this.close = closer;
}
DialogViewModel.prototype.onclose = function() {
this.closed.dispatch();
}
function FormDialogViewModel(title) {
DialogViewModel.call(this, title);
this.items = ko.observable([]);
this.isSubmitted = ko.observable(false);
this.showCancel = ko.observable(true);
}
inherits(FormDialogViewModel, DialogViewModel);
FormDialogViewModel.prototype.template = 'formDialog';
FormDialogViewModel.prototype.submit = function() {
this.isSubmitted(true);
this.close();
}
function CredentialsDialogViewModel() {
FormDialogViewModel.call(this);
this.title('Remote requires authentication');
this.taDialogName('credentials-dialog');
this.showCancel(false);
this.username = ko.observable();
this.password = ko.observable();
this.items([
{ name: 'Username', value: this.username, placeholder: 'Username', type: 'text', autofocus: true, taName: 'username' },
{ name: 'Password', value: this.password, placeholder: 'Password', type: 'password', autofocus: false, taName: 'password' }
]);
}
inherits(CredentialsDialogViewModel, FormDialogViewModel);
function AddRemoteDialogViewModel() {
FormDialogViewModel.call(this);
this.title('Add new remote');
this.taDialogName('add-remote');
this.name = ko.observable();
this.url = ko.observable();
this.items([
{ name: 'Name', value: this.name, placeholder: 'Name', type: 'text', autofocus: true, taName: 'name' },
{ name: 'Url', value: this.url, placeholder: 'Url', type: 'text', autofocus: false, taName: 'url' }
]);
}
inherits(AddRemoteDialogViewModel, FormDialogViewModel);
function AddSubmoduleDialogViewModel() {
FormDialogViewModel.call(this);
this.title('Add new submodule');
this.taDialogName('add-submodule');
this.path = ko.observable();
this.url = ko.observable();
this.items([
{ name: 'Path', value: this.path, placeholder: 'Path', type: 'text', autofocus: true, taName: 'path' },
{ name: 'Url', value: this.url, placeholder: 'Url', type: 'text', autofocus: false, taName: 'url' }
]);
}
inherits(AddSubmoduleDialogViewModel, FormDialogViewModel);
function PromptDialogViewModel(title, details) {
DialogViewModel.call(this, title);
this.alternatives = ko.observable();
this.details = ko.observable(details);
}
inherits(PromptDialogViewModel, DialogViewModel);
PromptDialogViewModel.prototype.template = 'prompt';
function YesNoDialogViewModel(title, details) {
PromptDialogViewModel.call(this, title, details);
var self = this;
this.taDialogName('yes-no-dialog');
this.result = ko.observable(false);
this.alternatives([
{ label: 'Yes', primary: true, taId: 'yes', click: function() { self.result(true); self.close(); } },
{ label: 'No', primary: false, taId: 'no', click: function() { self.result(false); self.close(); } },
]);
}
inherits(YesNoDialogViewModel, PromptDialogViewModel);
function YesNoMuteDialogViewModel(title, details) {
PromptDialogViewModel.call(this, title, details);
var self = this;
this.taDialogName('yes-no-mute-dialog');
this.result = ko.observable(false);
this.alternatives([
{ label: 'Yes', primary: true, taId: 'yes', click: function() { self.result(true); self.close(); } },
{ label: 'Yes and mute for awhile', primary: false, taId: 'mute', click: function() { self.result("mute"); self.close(); } },
{ label: 'No', primary: false, taId: 'no', click: function() { self.result(false); self.close(); } }
]);
}
inherits(YesNoMuteDialogViewModel, PromptDialogViewModel);
function TooManyFilesDialogViewModel(title, details) {
PromptDialogViewModel.call(this, title, details);
var self = this;
this.taDialogName('yes-no-dialog');
this.result = ko.observable(false);
this.alternatives([
{ label: "Don't load", primary: true, taId: 'noLoad', click: function() { self.result(false); self.close(); } },
{ label: 'Load anyway', primary: false, taId: 'loadAnyway', click: function() { self.result(true); self.close(); } },
]);
}
inherits(TooManyFilesDialogViewModel, PromptDialogViewModel);

View File

@ -1,26 +0,0 @@
<div class="modal fade" data-bind="modal: { onclose: onclose, closer: setCloser }, attr: { 'data-ta-container': taDialogName }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" data-bind="text: title"></h4>
</div>
<!-- Autocomplete is off here because of https://github.com/FredrikNoren/ungit/issues/363 -->
<form class="form-horizontal" data-bind="submit: submit" autocomplete="off">
<div class="modal-body" data-bind="foreach: items">
<div class="form-group">
<label data-bind="text: name, attr: { for: name }" class="col-lg-2 control-label"></label>
<div class="col-lg-10">
<input class="form-control" id="inputName"
data-bind="value: value, attr: { id: name, placeholder: placeholder, type: type, autofocus: autofocus, 'data-ta-input': taName }">
</div>
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-primary" value="Submit" data-ta-clickable="submit">
<button class="btn btn-default" data-bind="click: close, visible: showCancel">Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,18 +0,0 @@
<div class="modal fade" data-bind="modal: { onclose: onclose, closer: setCloser }, attr: { 'data-ta-container': taDialogName }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" data-bind="text: title"></h4>
</div>
<div class="modal-body">
<p data-bind="text: details"></p>
</div>
<div class="modal-footer">
<!-- ko foreach: alternatives -->
<button type="button" class="btn btn-default" data-bind="css: { 'btn-primary': primary }, text: label, click: click, attr: { 'data-ta-clickable': taId }"></button>
<!-- /ko -->
</div>
</div>
</div>
</div>

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"formDialog": "formDialog.html",
"prompt": "prompt.html"
},
"javascript": "dialogs.bundle.js"
}
}

View File

@ -1,65 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
var navigation = require('ungit-navigation');
components.register('gitErrors', function(args) {
return new GitErrorsViewModel(args.server, args.repoPath);
});
var GitErrorsViewModel = function(server, repoPath) {
var self = this;
this.server = server;
this.repoPath = repoPath;
this.gitErrors = ko.observableArray();
}
GitErrorsViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('gitErrors', this, {}, parentElement);
}
GitErrorsViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'git-error') this._handleGitError(event);
}
GitErrorsViewModel.prototype._handleGitError = function(event) {
if (event.data.repoPath != this.repoPath()) return;
this.gitErrors.push(new GitErrorViewModel(this, this.server, event.data));
}
function GitErrorViewModel(gitErrors, server, data) {
var self = this;
this.gitErrors = gitErrors;
this.server = server;
this.tip = data.tip;
this.command = data.command;
this.error = data.error;
this.stdout = data.stdout;
this.stderr = data.stderr;
this.showEnableBugtracking = ko.observable(false);
this.bugReportWasSent = ungit.config.bugtracking;
if (!data.shouldSkipReport && !ungit.config.bugtracking) {
this.server.get('/userconfig', undefined, function(err, userConfig) {
if (err) return;
self.showEnableBugtracking(!userConfig.bugtracking);
});
}
}
GitErrorViewModel.prototype.dismiss = function() {
this.gitErrors.gitErrors.remove(this);
}
GitErrorViewModel.prototype.enableBugtrackingAndStatistics = function() {
var self = this;
this.server.get('/userconfig', undefined, function(err, userConfig) {
if (err) return;
userConfig.bugtracking = true;
userConfig.sendUsageStatistics = true;
self.server.post('/userconfig', userConfig, function(err) {
if (err) return;
self.showEnableBugtracking(false);
});
});
}
},{"knockout":"knockout","ungit-components":"ungit-components","ungit-navigation":"ungit-navigation","ungit-program-events":"ungit-program-events"}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL2dpdEVycm9ycy9naXRFcnJvcnMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIlxudmFyIGtvID0gcmVxdWlyZSgna25vY2tvdXQnKTtcbnZhciBjb21wb25lbnRzID0gcmVxdWlyZSgndW5naXQtY29tcG9uZW50cycpO1xudmFyIHByb2dyYW1FdmVudHMgPSByZXF1aXJlKCd1bmdpdC1wcm9ncmFtLWV2ZW50cycpO1xudmFyIG5hdmlnYXRpb24gPSByZXF1aXJlKCd1bmdpdC1uYXZpZ2F0aW9uJyk7XG5cbmNvbXBvbmVudHMucmVnaXN0ZXIoJ2dpdEVycm9ycycsIGZ1bmN0aW9uKGFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBHaXRFcnJvcnNWaWV3TW9kZWwoYXJncy5zZXJ2ZXIsIGFyZ3MucmVwb1BhdGgpO1xufSk7XG5cbnZhciBHaXRFcnJvcnNWaWV3TW9kZWwgPSBmdW5jdGlvbihzZXJ2ZXIsIHJlcG9QYXRoKSB7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXI7XG4gIHRoaXMucmVwb1BhdGggPSByZXBvUGF0aDtcbiAgdGhpcy5naXRFcnJvcnMgPSBrby5vYnNlcnZhYmxlQXJyYXkoKTtcbn1cbkdpdEVycm9yc1ZpZXdNb2RlbC5wcm90b3R5cGUudXBkYXRlTm9kZSA9IGZ1bmN0aW9uKHBhcmVudEVsZW1lbnQpIHtcbiAga28ucmVuZGVyVGVtcGxhdGUoJ2dpdEVycm9ycycsIHRoaXMsIHt9LCBwYXJlbnRFbGVtZW50KTtcbn1cbkdpdEVycm9yc1ZpZXdNb2RlbC5wcm90b3R5cGUub25Qcm9ncmFtRXZlbnQgPSBmdW5jdGlvbihldmVudCkge1xuICBpZiAoZXZlbnQuZXZlbnQgPT0gJ2dpdC1lcnJvcicpIHRoaXMuX2hhbmRsZUdpdEVycm9yKGV2ZW50KTtcbn1cbkdpdEVycm9yc1ZpZXdNb2RlbC5wcm90b3R5cGUuX2hhbmRsZUdpdEVycm9yID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgaWYgKGV2ZW50LmRhdGEucmVwb1BhdGggIT0gdGhpcy5yZXBvUGF0aCgpKSByZXR1cm47XG4gIHRoaXMuZ2l0RXJyb3JzLnB1c2gobmV3IEdpdEVycm9yVmlld01vZGVsKHRoaXMsIHRoaXMuc2VydmVyLCBldmVudC5kYXRhKSk7XG59XG5cbmZ1bmN0aW9uIEdpdEVycm9yVmlld01vZGVsKGdpdEVycm9ycywgc2VydmVyLCBkYXRhKSB7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgdGhpcy5naXRFcnJvcnMgPSBnaXRFcnJvcnM7XG4gIHRoaXMuc2VydmVyID0gc2VydmVyO1xuICB0aGlzLnRpcCA9IGRhdGEudGlwO1xuICB0aGlzLmNvbW1hbmQgPSBkYXRhLmNvbW1hbmQ7XG4gIHRoaXMuZXJyb3IgPSBkYXRhLmVycm9yO1xuICB0aGlzLnN0ZG91dCA9IGRhdGEuc3Rkb3V0O1xuICB0aGlzLnN0ZGVyciA9IGRhdGEuc3RkZXJyO1xuICB0aGlzLnNob3dFbmFibGVCdWd0cmFja2luZyA9IGtvLm9ic2VydmFibGUoZmFsc2UpO1xuICB0aGlzLmJ1Z1JlcG9ydFdhc1NlbnQgPSB1bmdpdC5jb25maWcuYnVndHJhY2tpbmc7XG5cbiAgaWYgKCFkYXRhLnNob3VsZFNraXBSZXBvcnQgJiYgIXVuZ2l0LmNvbmZpZy5idWd0cmFja2luZykge1xuICAgIHRoaXMuc2VydmVyLmdldCgnL3VzZXJjb25maWcnLCB1bmRlZmluZWQsIGZ1bmN0aW9uKGVyciwgdXNlckNvbmZpZykge1xuICAgICAgaWYgKGVycikgcmV0dXJuO1xuICAgICAgc2VsZi5zaG93RW5hYmxlQnVndHJhY2tpbmcoIXVzZXJDb25maWcuYnVndHJhY2tpbmcpO1xuICAgIH0pO1xuICB9XG59XG5HaXRFcnJvclZpZXdNb2RlbC5wcm90b3R5cGUuZGlzbWlzcyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmdpdEVycm9ycy5naXRFcnJvcnMucmVtb3ZlKHRoaXMpO1xufVxuR2l0RXJyb3JWaWV3TW9kZWwucHJvdG90eXBlLmVuYWJsZUJ1Z3RyYWNraW5nQW5kU3RhdGlzdGljcyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHRoaXMuc2VydmVyLmdldCgnL3VzZXJjb25maWcnLCB1bmRlZmluZWQsIGZ1bmN0aW9uKGVyciwgdXNlckNvbmZpZykge1xuICAgIGlmIChlcnIpIHJldHVybjtcbiAgICB1c2VyQ29uZmlnLmJ1Z3RyYWNraW5nID0gdHJ1ZTtcbiAgICB1c2VyQ29uZmlnLnNlbmRVc2FnZVN0YXRpc3RpY3MgPSB0cnVlO1xuICAgIHNlbGYuc2VydmVyLnBvc3QoJy91c2VyY29uZmlnJywgdXNlckNvbmZpZywgZnVuY3Rpb24oZXJyKSB7XG4gICAgICBpZiAoZXJyKSByZXR1cm47XG4gICAgICBzZWxmLnNob3dFbmFibGVCdWd0cmFja2luZyhmYWxzZSk7XG4gICAgfSk7XG4gIH0pO1xufVxuIl19

View File

@ -1,22 +0,0 @@
<!-- ko foreach: gitErrors -->
<div class="alert alert-danger" data-ta-container="git-error-container">
<button type="button" class="close" data-bind="click: dismiss">&times;</button>
<h3><span class="glyphicon glyphicon-warning-sign"></span> Unhandled git error!</h3>
<p>
Ungit tried to run a git command that resulted in an unhandled error. <span data-bind="visible: bugReportWasSent">An automatic bug report was sent.</span>
</p>
<p data-bind="visible: tip, text: tip"></p>
<p data-bind="visible: showEnableBugtracking">
<button class="btn btn-primary" data-bind="click: enableBugtrackingAndStatistics">Enable bugtracking + usage statistics</button> to automatically report bugs, and/or <a href="https://github.com/FredrikNoren/ungit/issues" target="_blank">file an issue on the ungit issue tracker</a>.
</p>
<h4>Command</h4>
<pre data-bind="text: command"></pre>
<h4>Error</h4>
<pre data-bind="text: error"></pre>
<h4>Stderr</h4>
<pre data-bind="text: stderr"></pre>
<h4>Stdout</h4>
<pre data-bind="text: stdout"></pre>
</div>
<!-- /ko -->

View File

@ -1,61 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
var navigation = require('ungit-navigation');
components.register('gitErrors', function(args) {
return new GitErrorsViewModel(args.server, args.repoPath);
});
var GitErrorsViewModel = function(server, repoPath) {
var self = this;
this.server = server;
this.repoPath = repoPath;
this.gitErrors = ko.observableArray();
}
GitErrorsViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('gitErrors', this, {}, parentElement);
}
GitErrorsViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'git-error') this._handleGitError(event);
}
GitErrorsViewModel.prototype._handleGitError = function(event) {
if (event.data.repoPath != this.repoPath()) return;
this.gitErrors.push(new GitErrorViewModel(this, this.server, event.data));
}
function GitErrorViewModel(gitErrors, server, data) {
var self = this;
this.gitErrors = gitErrors;
this.server = server;
this.tip = data.tip;
this.command = data.command;
this.error = data.error;
this.stdout = data.stdout;
this.stderr = data.stderr;
this.showEnableBugtracking = ko.observable(false);
this.bugReportWasSent = ungit.config.bugtracking;
if (!data.shouldSkipReport && !ungit.config.bugtracking) {
this.server.get('/userconfig', undefined, function(err, userConfig) {
if (err) return;
self.showEnableBugtracking(!userConfig.bugtracking);
});
}
}
GitErrorViewModel.prototype.dismiss = function() {
this.gitErrors.gitErrors.remove(this);
}
GitErrorViewModel.prototype.enableBugtrackingAndStatistics = function() {
var self = this;
this.server.get('/userconfig', undefined, function(err, userConfig) {
if (err) return;
userConfig.bugtracking = true;
userConfig.sendUsageStatistics = true;
self.server.post('/userconfig', userConfig, function(err) {
if (err) return;
self.showEnableBugtracking(false);
});
});
}

View File

@ -1,8 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"gitErrors": "gitErrors.html"
},
"javascript": "gitErrors.bundle.js"
}
}

View File

@ -1,22 +0,0 @@
var ko = require('knockout');
require('mina');
module.exports = function(graph) {
var self = this;
this.element = ko.observable();
this.previousGraph = undefined;
this.element.subscribe(function(val) {
if (val) self.animate(true);
});
this.animate = function(forceRefresh) {
var currentGraph = this.getGraphAttr();
// animate only when dom is valid and (attribute changed or force refresh due to dom change)
if (this.element() && (forceRefresh || JSON.stringify(currentGraph) !== JSON.stringify(this.previousGraph))) {
var now = Date.now();
window.mina(this.previousGraph || currentGraph, currentGraph, now, now + 750, window.mina.time, function (val) {
self.setGraphAttr(val);
}, window.mina.elastic);
this.previousGraph = currentGraph;
}
}
};

View File

@ -1,26 +0,0 @@
var ko = require('knockout');
var Animateable = require('./animateable');
var EdgeViewModel = function(graph, nodeAsha1, nodeBsha1) {
var self = this;
Animateable.call(this);
this.nodeA = graph.getNode(nodeAsha1);
this.nodeB = graph.getNode(nodeBsha1);
this.getGraphAttr = ko.computed(function() {
if (self.nodeB.isInited && self.nodeB.cx() && self.nodeB.cy()) {
return [self.nodeA.cx(), self.nodeA.cy(), self.nodeA.cx(), self.nodeA.cy(),
self.nodeB.cx(), self.nodeB.cy(), self.nodeB.cx(), self.nodeB.cy()];
} else if (graph.graphHeight()) {
return [self.nodeA.cx(), self.nodeA.cy(), self.nodeA.cx(), self.nodeA.cy(),
self.nodeA.cx(), graph.graphHeight(), self.nodeA.cx(), graph.graphHeight()];
} else {
return [self.nodeA.cx(), self.nodeA.cy(), self.nodeA.cx(), self.nodeA.cy(),
self.nodeA.cx(), self.nodeA.cy(), self.nodeA.cx(), self.nodeA.cy()];
}
});
this.getGraphAttr.subscribe(this.animate.bind(this));
}
EdgeViewModel.prototype.setGraphAttr = function(val) {
this.element().setAttribute('d', 'M' + val.slice(0,4).join(',') + 'L' + val.slice(4,8).join(','));
}
module.exports = EdgeViewModel;

View File

@ -1,353 +0,0 @@
var ko = require('knockout');
var inherits = require('util').inherits;
var components = require('ungit-components');
var RefViewModel = require('./git-ref.js');
var HoverActions = require('./hover-actions');
var RebaseViewModel = HoverActions.RebaseViewModel;
var MergeViewModel = HoverActions.MergeViewModel;
var ResetViewModel = HoverActions.ResetViewModel;
var PushViewModel = HoverActions.PushViewModel;
var programEvents = require('ungit-program-events');
var GraphActions = {};
module.exports = GraphActions;
GraphActions.ActionBase = function(graph) {
var self = this;
this.graph = graph;
this.server = graph.server;
this.performProgressBar = components.create('progressBar', {
predictionMemoryKey: 'action-' + this.style + '-' + graph.repoPath(),
fallbackPredictedTimeMs: 1000,
temporary: true
});
this.isHighlighted = ko.computed(function() {
return !graph.hoverGraphAction() || graph.hoverGraphAction() == self;
});
this.cssClasses = ko.computed(function() {
var c = self.style;
if (!self.isHighlighted()) c += ' dimmed';
return c;
})
}
GraphActions.ActionBase.prototype.icon = null;
GraphActions.ActionBase.prototype.doPerform = function() {
var self = this;
this.graph.hoverGraphAction(null);
self.performProgressBar.start();
this.perform(function() {
self.performProgressBar.stop();
});
}
GraphActions.ActionBase.prototype.dragEnter = function() {
if (!this.visible()) return;
this.graph.hoverGraphAction(this);
}
GraphActions.ActionBase.prototype.dragLeave = function() {
if (!this.visible()) return;
this.graph.hoverGraphAction(null);
}
GraphActions.ActionBase.prototype.mouseover = function() {
this.graph.hoverGraphAction(this);
}
GraphActions.ActionBase.prototype.mouseout = function() {
this.graph.hoverGraphAction(null);
}
GraphActions.Move = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
return self.graph.currentActionContext() instanceof RefViewModel &&
self.graph.currentActionContext().node() != self.node;
});
}
inherits(GraphActions.Move, GraphActions.ActionBase);
GraphActions.Move.prototype.text = 'Move';
GraphActions.Move.prototype.style = 'move';
GraphActions.Move.prototype.icon = 'glyphicon glyphicon-move';
GraphActions.Move.prototype.perform = function(callback) {
this.graph.currentActionContext().moveTo(this.node.sha1, callback);
}
GraphActions.Reset = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
if (!(self.graph.currentActionContext() instanceof RefViewModel)) return false;
var context = self.graph.currentActionContext();
if (context.node() != self.node) return false;
var remoteRef = context.getRemoteRef(self.graph.currentRemote());
return remoteRef &&
remoteRef.node() != context.node() &&
remoteRef.node().date < context.node().date;
});
}
inherits(GraphActions.Reset, GraphActions.ActionBase);
GraphActions.Reset.prototype.text = 'Reset';
GraphActions.Reset.prototype.style = 'reset';
GraphActions.Reset.prototype.icon = 'glyphicon glyphicon-trash';
GraphActions.Reset.prototype.createHoverGraphic = function() {
var context = this.graph.currentActionContext();
if (!context) return null;
var remoteRef = context.getRemoteRef(this.graph.currentRemote());
var nodes = context.node().getPathToCommonAncestor(remoteRef.node()).slice(0, -1);
return new ResetViewModel(nodes);
}
GraphActions.Reset.prototype.perform = function(callback) {
var self = this;
var context = this.graph.currentActionContext();
var remoteRef = context.getRemoteRef(self.graph.currentRemote());
var diag = components.create('yesnodialog', { title: 'Are you sure?', details: 'Resetting to ref: ' + remoteRef.name + ' cannot be undone with ungit.'});
diag.closed.add(function() {
if (diag.result()) {
self.server.post('/reset', { path: self.graph.repoPath(), to: remoteRef.name, mode: 'hard' }, function() {
context.node(remoteRef.node());
callback();
});
} else {
callback();
}
});
programEvents.dispatch({ event: 'request-show-dialog', dialog: diag });
}
GraphActions.Rebase = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
return self.graph.currentActionContext() instanceof RefViewModel &&
(!ungit.config.showRebaseAndMergeOnlyOnRefs || self.node.refs().length > 0) &&
self.graph.currentActionContext().current() &&
self.graph.currentActionContext().node() != self.node;
});
}
inherits(GraphActions.Rebase, GraphActions.ActionBase);
GraphActions.Rebase.prototype.text = 'Rebase';
GraphActions.Rebase.prototype.style = 'rebase';
GraphActions.Rebase.prototype.icon = 'octicon octicon-repo-forked flip';
GraphActions.Rebase.prototype.createHoverGraphic = function() {
var onto = this.graph.currentActionContext();
if (!onto) return;
if (onto instanceof RefViewModel) onto = onto.node();
var path = onto.getPathToCommonAncestor(this.node);
return new RebaseViewModel(this.node, path);
}
GraphActions.Rebase.prototype.perform = function(callback) {
this.server.post('/rebase', { path: this.graph.repoPath(), onto: this.node.sha1 }, function(err) {
callback();
if (err && err.errorCode == 'merge-failed') return true;
});
}
GraphActions.Merge = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
if (!self.graph.checkedOutRef() || !self.graph.checkedOutRef().node()) return false;
return self.graph.currentActionContext() instanceof RefViewModel &&
!self.graph.currentActionContext().current() &&
self.graph.checkedOutRef().node() == self.node;
});
}
inherits(GraphActions.Merge, GraphActions.ActionBase);
GraphActions.Merge.prototype.text = 'Merge';
GraphActions.Merge.prototype.style = 'merge';
GraphActions.Merge.prototype.icon = 'octicon octicon-git-merge';
GraphActions.Merge.prototype.createHoverGraphic = function() {
var node = this.graph.currentActionContext();
if (!node) return null;
if (node instanceof RefViewModel) node = node.node();
return new MergeViewModel(this.graph, this.node, node);
}
GraphActions.Merge.prototype.perform = function(callback) {
this.server.post('/merge', { path: this.graph.repoPath(), with: this.graph.currentActionContext().localRefName }, function(err) {
callback();
if (err && err.errorCode == 'merge-failed') return true;
});
}
GraphActions.Push = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
return self.graph.currentActionContext() instanceof RefViewModel &&
self.graph.currentActionContext().node() == self.node &&
self.graph.currentActionContext().canBePushed(self.graph.currentRemote());
});
}
inherits(GraphActions.Push, GraphActions.ActionBase);
GraphActions.Push.prototype.text = 'Push';
GraphActions.Push.prototype.style = 'push';
GraphActions.Push.prototype.icon = 'octicon octicon-cloud-upload';
GraphActions.Push.prototype.createHoverGraphic = function() {
var context = this.graph.currentActionContext();
if (!context) return null;
var remoteRef = context.getRemoteRef(this.graph.currentRemote());
if (!remoteRef) return null;
return new PushViewModel(remoteRef.node(), context.node());
}
GraphActions.Push.prototype.perform = function(callback) {
var self = this;
var ref = this.graph.currentActionContext();
var remoteRef = ref.getRemoteRef(this.graph.currentRemote());
if (remoteRef) {
remoteRef.moveTo(ref.node().sha1, callback);
} else ref.createRemoteRef(function(err) {
if (!err && self.graph.HEAD().name == ref.name) {
self.grah.HEADref().node(ref.node());
}
callback();
});
}
GraphActions.Checkout = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
if (self.graph.currentActionContext() instanceof RefViewModel)
return self.graph.currentActionContext().node() == self.node &&
!self.graph.currentActionContext().current();
return ungit.config.allowCheckoutNodes &&
self.graph.currentActionContext() == self.node;
});
}
inherits(GraphActions.Checkout, GraphActions.ActionBase);
GraphActions.Checkout.prototype.text = 'Checkout';
GraphActions.Checkout.prototype.style = 'checkout';
GraphActions.Checkout.prototype.icon = 'octicon octicon-desktop-download';
GraphActions.Checkout.prototype.perform = function(callback) {
var self = this;
var context = this.graph.currentActionContext();
var refName = context instanceof RefViewModel ? context.refName : context.sha1;
this.server.post('/checkout', { path: this.graph.repoPath(), name: refName }, function(err) {
if (err && err.errorCode != 'merge-failed') {
callback();
return;
}
if (context instanceof RefViewModel && context.isRemoteBranch) {
self.server.post('/reset', { path: self.graph.repoPath(), to: context.name, mode: 'hard' }, function(err, res) {
self.graph.HEADref().node(context instanceof RefViewModel ? context.node() : context);
callback();
return err && err.errorCode != 'merge-failed' ? undefined : true;
});
} else {
self.graph.HEADref().node(context instanceof RefViewModel ? context.node() : context);
callback();
}
return true;
});
}
GraphActions.Delete = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
return self.graph.currentActionContext() instanceof RefViewModel &&
self.graph.currentActionContext().node() == self.node &&
!self.graph.currentActionContext().current();
});
}
inherits(GraphActions.Delete, GraphActions.ActionBase);
GraphActions.Delete.prototype.text = 'Delete';
GraphActions.Delete.prototype.style = 'delete';
GraphActions.Delete.prototype.icon = 'glyphicon glyphicon-remove';
GraphActions.Delete.prototype.perform = function(callback) {
var context = this.graph.currentActionContext();
var name = context.isRemoteBranch ? "remote " + context.localRefName : context.localRefName;
var diag = components.create('yesnodialog', { title: 'Are you sure?', details: 'Deleting ' + name + ' branch or tag cannot be undone with ungit.'});
diag.closed.add(function() {
if (diag.result()) {
context.remove(callback);
} else {
callback();
}
});
programEvents.dispatch({ event: 'request-show-dialog', dialog: diag });
}
GraphActions.CherryPick = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
var context = self.graph.currentActionContext();
return context === self.node && self.graph.HEAD() && context.sha1 !== self.graph.HEAD().sha1
});
}
inherits(GraphActions.CherryPick, GraphActions.ActionBase);
GraphActions.CherryPick.prototype.text = 'Cherry pick';
GraphActions.CherryPick.prototype.style = 'cherry-pick';
GraphActions.CherryPick.prototype.icon = 'octicon octicon-circuit-board';
GraphActions.CherryPick.prototype.perform = function(callback) {
var self = this;
this.server.post('/cherrypick', { path: this.graph.repoPath(), name: this.node.sha1 }, function(err) {
callback();
if (err && err.errorCode == 'merge-failed') return true;
});
}
GraphActions.Uncommit = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
return self.graph.currentActionContext() == self.node &&
self.graph.HEAD() == self.node;
});
}
inherits(GraphActions.Uncommit, GraphActions.ActionBase);
GraphActions.Uncommit.prototype.text = 'Uncommit';
GraphActions.Uncommit.prototype.style = 'uncommit';
GraphActions.Uncommit.prototype.icon = 'octicon octicon-zap';
GraphActions.Uncommit.prototype.perform = function(callback) {
var self = this;
this.server.postPromise('/reset', { path: this.graph.repoPath(), to: 'HEAD^', mode: 'mixed' })
.then(function() {
var targetNode = self.node.belowNode;
while (targetNode && !targetNode.ancestorOfHEAD()) {
targetNode = targetNode.belowNode;
}
self.graph.HEADref().node(targetNode ? targetNode : null);
self.graph.checkedOutRef().node(targetNode ? targetNode : null);
}).finally(callback);
}
GraphActions.Revert = function(graph, node) {
var self = this;
GraphActions.ActionBase.call(this, graph);
this.node = node;
this.visible = ko.computed(function() {
if (self.performProgressBar.running()) return true;
return self.graph.currentActionContext() == self.node;
});
}
inherits(GraphActions.Revert, GraphActions.ActionBase);
GraphActions.Revert.prototype.text = 'Revert';
GraphActions.Revert.prototype.style = 'revert';
GraphActions.Revert.prototype.icon = 'octicon octicon-history';
GraphActions.Revert.prototype.perform = function(callback) {
var self = this;
this.server.postPromise('/revert', { path: this.graph.repoPath(), commit: this.node.sha1 })
.finally(callback);
}

View File

@ -1,258 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var Selectable = require('./selectable');
var Animateable = require('./animateable');
var programEvents = require('ungit-program-events');
var GraphActions = require('./git-graph-actions');
var GitNodeViewModel = function(graph, sha1) {
var self = this;
Selectable.call(this, graph);
Animateable.call(this);
this.graph = graph;
this.sha1 = sha1;
this.isInited = false;
this.title = undefined;
this.parents = ko.observableArray();
this.commitTime = undefined; // commit time in string
this.date = undefined; // commit time in numeric format for sort
this.color = ko.observable();
this.ideologicalBranch = ko.observable();
this.remoteTags = ko.observableArray();
this.branchesAndLocalTags = ko.observableArray();
this.refs = ko.computed(function() {
var rs = self.branchesAndLocalTags().concat(self.remoteTags());
rs.sort(function(a, b) {
if (a.isLocal && !b.isLocal) return -1;
if (!a.isLocal && b.isLocal) return 1;
return a.refName < b.refName ? -1 : 1;
});
return rs;
});
this.ancestorOfHEAD = ko.observable(false);
this.nodeIsMousehover = ko.observable(false);
this.commitContainerVisible = ko.computed(function() {
return self.ancestorOfHEAD() || self.nodeIsMousehover() || self.selected();
});
this.highlighted = ko.computed(function() {
return self.nodeIsMousehover() || self.selected();
});
this.selected.subscribe(function() {
programEvents.dispatch({ event: 'graph-render' });
});
this.commitComponent = components.create('commit', {
sha1: this.sha1,
repoPath: this.graph.repoPath,
server: this.graph.server,
selected: this.selected,
highlighted: this.highlighted,
nodeIsMousehover: this.nodeIsMousehover
});
// These are split up like this because branches and local tags can be found in the git log,
// whereas remote tags needs to be fetched with another command (which is much slower)
this.branches = ko.computed(function() {
return self.refs().filter(function(r) { return r.isBranch; });
});
this.tags = ko.computed(function() {
return self.refs().filter(function(r) { return r.isTag; });
});
this.showNewRefAction = ko.computed(function() {
return !graph.currentActionContext();
});
this.newBranchName = ko.observable();
this.newBranchNameHasFocus = ko.observable(true);
this.newBranchNameHasFocus.subscribe(function(newValue) {
if (!newValue) {
// Small timeout because in ff the form is hidden before the submit click event is registered otherwise
setTimeout(function() {
self.branchingFormVisible(false);
}, 200);
}
});
this.branchingFormVisible = ko.observable(false);
this.canCreateRef = ko.computed(function() {
return self.newBranchName() && self.newBranchName().trim() && self.newBranchName().indexOf(' ') == -1;
});
this.branchOrder = ko.observable();
this.aboveNode = undefined;
this.belowNode = undefined;
this.r = ko.observable();
this.cx = ko.observable();
this.cy = ko.observable();
this.dropareaGraphActions = [
new GraphActions.Move(this.graph, this),
new GraphActions.Rebase(this.graph, this),
new GraphActions.Merge(this.graph, this),
new GraphActions.Push(this.graph, this),
new GraphActions.Reset(this.graph, this),
new GraphActions.Checkout(this.graph, this),
new GraphActions.Delete(this.graph, this),
new GraphActions.CherryPick(this.graph, this),
new GraphActions.Uncommit(this.graph, this),
new GraphActions.Revert(this.graph, this)
];
}
module.exports = GitNodeViewModel;
GitNodeViewModel.prototype.getGraphAttr = function() {
return [this.cx(), this.cy()];
}
GitNodeViewModel.prototype.setGraphAttr = function(val) {
this.element().setAttribute('x', val[0] - 30);
this.element().setAttribute('y', val[1] - 30);
}
GitNodeViewModel.prototype.render = function() {
if (!this.isInited) return;
if (this.ancestorOfHEAD()) {
this.r(30);
this.cx(610);
if (!this.aboveNode) {
this.cy(120);
} else if (this.aboveNode.ancestorOfHEAD()) {
this.cy(this.aboveNode.cy() + 120);
} else {
this.cy(this.aboveNode.cy() + 60);
}
} else {
this.r(15);
this.cx(610 + (90 * this.branchOrder()));
this.cy(this.aboveNode ? this.aboveNode.cy() + 60 : 120);
}
if (this.aboveNode && this.aboveNode.selected()) {
this.cy(this.aboveNode.cy() + this.aboveNode.commitComponent.element().offsetHeight + 30);
}
this.commitComponent.selectedDiffLeftPosition(-(this.cx() - 600));
this.color(this.ideologicalBranch() ? this.ideologicalBranch().color : '#666');
this.animate();
}
GitNodeViewModel.prototype.setData = function(logEntry) {
var self = this;
this.title = logEntry.message.split('\n')[0];
this.parents(logEntry.parents || []);
this.commitTime = logEntry.commitDate;
this.date = Date.parse(this.commitTime);
this.commitComponent.setData(logEntry);
if (logEntry.refs) {
logEntry.refs.forEach(function(ref) {
self.graph.getRef(ref).node(self);
});
}
this.isInited = true;
}
GitNodeViewModel.prototype.showBranchingForm = function() {
this.branchingFormVisible(true);
this.newBranchNameHasFocus(true);
}
GitNodeViewModel.prototype.createBranch = function() {
if (!this.canCreateRef()) return;
var self = this;
var command = ungit.config.autoCheckoutOnBranchCreate ? "/checkout" : "/branches";
this.graph.server.postPromise(command, { path: this.graph.repoPath(), name: this.newBranchName(), sha1: this.sha1 })
.then(function() {
self.graph.getRef('refs/heads/' + self.newBranchName()).node(self);
}).finally(function() {
self.branchingFormVisible(false);
self.newBranchName('');
programEvents.dispatch({ event: 'branch-updated' });
});
}
GitNodeViewModel.prototype.createTag = function() {
if (!this.canCreateRef()) return;
var self = this;
this.graph.server.postPromise('/tags', { path: this.graph.repoPath(), name: this.newBranchName(), sha1: this.sha1 })
.then(function() {
var newRef = self.graph.getRef('tag: refs/tags/' + self.newBranchName());
newRef.node(self);
}).finally(function() {
self.branchingFormVisible(false);
self.newBranchName('');
});
}
GitNodeViewModel.prototype.toggleSelected = function() {
var self = this;
var beforeThisCR = this.commitComponent.element().getBoundingClientRect();
var beforeBelowCR = null;
if (this.belowNode) {
beforeBelowCR = this.belowNode.commitComponent.element().getBoundingClientRect();
}
var prevSelected = this.graph.currentActionContext();
if (!(prevSelected instanceof GitNodeViewModel)) prevSelected = null;
var prevSelectedCR = prevSelected ? prevSelected.commitComponent.element().getBoundingClientRect() : null;
this.selected(!this.selected());
// If we are deselecting
if (!this.selected()) {
if (beforeThisCR.top < 0 && beforeBelowCR) {
var afterBelowCR = this.belowNode.commitComponent.element().getBoundingClientRect();
// If the next node is showing, try to keep it in the screen (no jumping)
if (beforeBelowCR.top < window.innerHeight) {
window.scrollBy(0, afterBelowCR.top - beforeBelowCR.top);
// Otherwise just try to bring them to the middle of the screen
} else {
window.scrollBy(0, afterBelowCR.top - window.innerHeight / 2);
}
}
// If we are selecting
} else {
var afterThisCR = this.commitComponent.element().getBoundingClientRect();
if ((prevSelectedCR && (prevSelectedCR.top < 0 || prevSelectedCR.top > window.innerHeight)) &&
afterThisCR.top != beforeThisCR.top) {
window.scrollBy(0, -(beforeThisCR.top - afterThisCR.top));
console.log('Fix')
}
}
return false;
}
GitNodeViewModel.prototype.removeRef = function(ref) {
if (ref.isRemoteTag) {
this.remoteTags.remove(ref);
} else {
this.branchesAndLocalTags.remove(ref);
}
}
GitNodeViewModel.prototype.pushRef = function(ref) {
if (ref.isRemoteTag && this.remoteTags.indexOf(ref) < 0) {
this.remoteTags.push(ref);
} else if(this.branchesAndLocalTags.indexOf(ref) < 0) {
this.branchesAndLocalTags.push(ref);
}
}
GitNodeViewModel.prototype.getPathToCommonAncestor = function(node) {
var path = [];
var thisNode = this;
while (thisNode && !node.isAncestor(thisNode)) {
path.push(thisNode);
thisNode = this.graph.nodesById[thisNode.parents()[0]];
}
if (thisNode) path.push(thisNode);
return path;
}
GitNodeViewModel.prototype.isAncestor = function(node) {
if (node == this) return true;
for (var v in this.parents()) {
var n = this.graph.nodesById[this.parents()[v]];
if (n && n.isAncestor(node)) return true;
}
return false;
}
GitNodeViewModel.prototype.getRightToLeftStrike = function() {
return 'M ' + (this.cx() - 30) + ' ' + (this.cy() - 30) + ' L ' + (this.cx() + 30) + ' ' + (this.cy() + 30);
}
GitNodeViewModel.prototype.getLeftToRightStrike = function() {
return 'M ' + (this.cx() + 30) + ' ' + (this.cy() - 30) + ' L ' + (this.cx() - 30) + ' ' + (this.cy() + 30);
}
GitNodeViewModel.prototype.nodeMouseover = function() {
this.nodeIsMousehover(true);
}
GitNodeViewModel.prototype.nodeMouseout = function() {
this.nodeIsMousehover(false);
}

View File

@ -1,168 +0,0 @@
var ko = require('knockout');
var md5 = require('blueimp-md5');
var Selectable = require('./selectable');
var programEvents = require('ungit-program-events');
var components = require('ungit-components');
var RefViewModel = function(fullRefName, graph) {
var self = this;
Selectable.call(this, graph);
this.graph = graph;
this.name = fullRefName;
this.node = ko.observable();
this.localRefName = this.name; // origin/master or master
this.refName = this.name; // master
this.isRemoteTag = this.name.indexOf('remote-tag: ') == 0;
this.isLocalTag = this.name.indexOf('tag: ') == 0;
this.isTag = this.isLocalTag || this.isRemoteTag;
var isRemoteBranchOrHEAD = this.name.indexOf('refs/remotes/') == 0;
this.isLocalHEAD = this.name == 'HEAD';
this.isRemoteHEAD = this.name.indexOf('/HEAD') != -1;
this.isLocalBranch = this.name.indexOf('refs/heads/') == 0;
this.isRemoteBranch = isRemoteBranchOrHEAD && !this.isRemoteHEAD;
this.isStash = this.name.indexOf('refs/stash') == 0;
this.isHEAD = this.isLocalHEAD || this.isRemoteHEAD;
this.isBranch = this.isLocalBranch || this.isRemoteBranch;
this.isRemote = isRemoteBranchOrHEAD || this.isRemoteTag;
this.isLocal = this.isLocalBranch || this.isLocalTag;
if (this.isLocalBranch) {
this.localRefName = this.name.slice('refs/heads/'.length);
this.refName = this.localRefName;
}
if (this.isRemoteBranch) {
this.localRefName = this.name.slice('refs/remotes/'.length);
}
if (this.isLocalTag) {
this.localRefName = this.name.slice('tag: refs/tags/'.length);
this.refName = this.localRefName;
}
if (this.isRemoteTag) {
this.localRefName = this.name.slice('remote-tag: '.length);
}
if (this.isRemote) {
// get rid of the origin/ part of origin/branchname
var s = this.localRefName.split('/');
this.remote = s[0];
this.refName = s.slice(1).join('/');
}
this.show = true;
this.server = this.graph.server;
this.isDragging = ko.observable(false);
this.current = ko.computed(function() {
return self.isLocalBranch && self.graph.checkedOutBranch() == self.refName;
});
this.color = this._colorFromHashOfString(this.name);
this.node.subscribe(function(oldNode) {
if (oldNode) oldNode.removeRef(self);
}, null, "beforeChange");
this.node.subscribe(function(newNode) {
if (newNode) newNode.pushRef(self);
});
};
module.exports = RefViewModel;
RefViewModel.prototype._colorFromHashOfString = function(string) {
return '#' + md5(string).toString().slice(0, 6);
}
RefViewModel.prototype.dragStart = function() {
this.graph.currentActionContext(this);
this.isDragging(true);
if (document.activeElement) document.activeElement.blur();
}
RefViewModel.prototype.dragEnd = function() {
this.graph.currentActionContext(null);
this.isDragging(false);
}
RefViewModel.prototype.moveTo = function(target, callback) {
var self = this;
var callbackWithRefSet = function(err, res) {
if (err) {
callback(err, res);
} else {
var targetNode = self.graph.getNode(target);
if (self.graph.checkedOutBranch() == self.refName) {
self.graph.HEADref().node(targetNode);
}
self.node(targetNode);
callback();
}
}
if (this.isLocal) {
if (this.current()) {
this.server.post('/reset', { path: this.graph.repoPath(), to: target, mode: 'hard' }, callbackWithRefSet);
} else if (this.isTag) {
this.server.post('/tags', { path: this.graph.repoPath(), name: this.refName, sha1: target, force: true }, callbackWithRefSet);
} else {
this.server.post('/branches', { path: this.graph.repoPath(), name: this.refName, sha1: target, force: true }, callbackWithRefSet);
}
} else {
var pushReq = { path: this.graph.repoPath(), remote: this.remote, refSpec: target, remoteBranch: this.refName };
this.server.post('/push', pushReq, function(err, res) {
if (err) {
if (err.errorCode == 'non-fast-forward') {
var forcePushDialog = components.create('yesnodialog', { title: 'Force push?', details: 'The remote branch can\'t be fast-forwarded.' });
forcePushDialog.closed.add(function() {
if (!forcePushDialog.result()) return callback();
pushReq.force = true;
self.server.post('/push', pushReq, callbackWithRefSet);
});
programEvents.dispatch({ event: 'request-show-dialog', dialog: forcePushDialog });
return true;
}
}
callbackWithRefSet(err, res);
});
}
}
RefViewModel.prototype.remove = function(callback) {
var self = this;
var url = this.isTag ? '/tags' : '/branches';
if (this.isRemote) url = '/remote' + url;
this.server.del(url, { path: this.graph.repoPath(), remote: this.isRemote ? this.remote : null, name: this.refName }, function(err) {
if (!err) {
self.node().removeRef(self);
self.graph.refsByRefName[self.name] = undefined;
}
callback();
self.graph.loadNodesFromApi();
if (url == '/remote/tags') {
programEvents.dispatch({ event: 'request-fetch-tags' });
} else {
programEvents.dispatch({ event: 'branch-updated' });
}
});
}
RefViewModel.prototype.getRemoteRef = function(remote) {
return this.graph.getRef(this.getRemoteRefFullName(remote), false);
}
RefViewModel.prototype.getRemoteRefFullName = function(remote) {
if (this.isLocalBranch) return 'refs/remotes/' + remote + '/' + this.refName;
if (this.isLocalTag) return 'remote-tag: ' + remote + '/' + this.refName;
return null;
}
RefViewModel.prototype.canBePushed = function(remote) {
if (!this.isLocal) return false;
var remoteRef = this.getRemoteRef(remote);
if (!remoteRef) return true;
return this.node() != remoteRef.node();
}
RefViewModel.prototype.createRemoteRef = function(callback) {
var self = this;
this.server.post('/push', { path: this.graph.repoPath(), remote: this.graph.currentRemote(),
refSpec: this.refName, remoteBranch: this.refName }, function(err) {
if (!err) {
var newRef = self.graph.getRef("refs/remotes/" + self.graph.currentRemote() + "/" + self.refName);
newRef.node(self.node());
}
callback(err);
});
}

View File

@ -1,55 +0,0 @@
<svg class="graphLog" xmlns="http://www.w3.org/2000/svg" version="1.1" data-bind="attr: { width: graphWidth, height: graphHeight }">
<defs>
<marker id="rebaseArrowEnd"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#22252E" />
</marker>
<marker id="pushArrowEnd"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="rgb(61, 139, 255)" />
</marker>
</defs>
<g>
<!-- ko if: commitNodeEdge -->
<g data-bind="attr: { opacity: commitOpacity }">
<path data-bind="attr: { d: commitNodeEdge }", stroke="#4A4A4A" stroke-width="8" stroke-dasharray="10, 5" />
<circle data-bind="attr: { stroke: commitNodeColor }" cx="610" cy="35" r="30" data-bind="attr: { stroke: '#4A4A4A' }" stroke-dasharray="10, 7" stroke-width="10" fill="transparent"/>
</g>
<!-- /ko -->
<!-- ko with: hoverGraphActionGraphic -->
<!-- ko foreach: bgEdges -->
<path data-bind="attr: { d: d, stroke: stroke, 'stroke-width': strokeWidth, 'stroke-dasharray': strokeDasharray, 'marker-end': markerEnd }" />
<!-- /ko -->
<!-- /ko -->
<!-- ko foreach: edges -->
<path data-bind="element: element" stroke="#4A4A4A" stroke-width="8" />
<!-- /ko -->
<!-- ko foreach: nodes -->
<svg data-bind="element: element" >
<circle data-bind="attr: { r: r, fill: color, 'data-ta-clickable': 'node-clickable-' + $index() }, event: { mouseover: nodeMouseover, mouseout: nodeMouseout }, click: toggleSelected" cx="30" cy="30" />
<!-- ko if: selected -->
<circle data-bind=" attr: { r: r() - 4 }, click: toggleSelected" stroke="#252833" stroke-width="4" fill="transparent" cx="30" cy="30" />
<!-- /ko -->
</svg>
<!-- /ko -->
<!-- ko with: hoverGraphActionGraphic -->
<!-- ko foreach: nodes -->
<circle data-bind="attr: { cx: cx, cy: cy, r: r, fill: fill, stroke: stroke, 'stroke-width': strokeWidth, 'stroke-dasharray': strokeDasharray }" />
<!-- /ko -->
<!-- ko foreach: fgEdges -->
<path data-bind="attr: { d: d, stroke: stroke, 'stroke-width': strokeWidth, 'stroke-dasharray': strokeDasharray, 'marker-end': markerEnd }" />
<!-- /ko -->
<!-- /ko -->
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,186 +0,0 @@
.graph {
position: relative;
display: inline-block;
width: 100%;
}
.graph .graphLog {
left: 575px;
}
.graph .nodeContainer {
position: absolute;
left: 30px;
top: 120px;
width: 100%;
}
.graph .nodeContainer .commit-container {
position: absolute;
top: -43px;
}
.graph .nodeContainer .rightSideContainer {
position: absolute;
display: flex;
margin-left: 440px;
top: -22px;
white-space: nowrap;
height: 40px;
}
.graph .nodeContainer .droparea {
margin: 8px 0px 4px 0px;
}
.graph .nodeContainer .ref {
display: inline-block;
opacity: 0.6;
cursor: move;
margin: 7px 0px 4px 0px;
outline: none;
padding: 2px 5px 2px 5px;
border: 3px solid transparent;
border-radius: 10px;
transition: border-color 0.5s;
-webkit-transition: border-color 0.5s;
}
.graph .nodeContainer .ref.dragging.focused {
border-color: transparent;
}
.graph .nodeContainer .ref.focused {
border-color: #fff;
}
.graph .nodeContainer .ref.current {
font-weight: bold;
opacity: 1;
font-size: 20px;
margin-top: 2px;
margin-bottom: -2px;
}
.graph .nodeContainer .ref.current .octicon {
font-size: 20px;
}
.graph .nodeContainer .ref.remote {
color: #5DB4FF;
}
.graph .nodeContainer .ref.tag {
color: #EEF266;
}
.graph .nodeContainer .graphAction {
color: #fff;
cursor: pointer;
opacity: 1;
transition: all 0.5s ease 0.2s;
-webkit-transition: all 0.5s ease 0.2s;
transition-property: opacity, max-width;
-webkit-transition-property: opacity, max-width;
margin-right: 2.5px;
margin-left: 2.5px;
overflow: hidden;
}
.graph .nodeContainer .graphAction .icon {
margin-right: 6px;
}
.graph .nodeContainer .graphAction .icon.flip {
-webkit-transform: rotate(-180deg);
-moz-transform: rotate(-180deg);
-o-transform: rotate(-180deg);
transform: rotate(-180deg);
}
.graph .nodeContainer .graphAction .icon.octicon-git-merge,
.graph .nodeContainer .graphAction .icon.octicon-repo-forked,
.graph .nodeContainer .graphAction .icon.octicon-zap {
margin-left: 3px;
}
.graph .nodeContainer .graphAction .icon.octicon-history {
margin-left: 2px;
}
.graph .nodeContainer .graphAction .icon.glyphicon-remove,
.graph .nodeContainer .graphAction .icon.octicon-circuit-board {
margin-left: 1px;
}
.graph .nodeContainer .graphAction.droparea {
padding-left: 7px;
}
.graph .nodeContainer .graphAction.dimmed {
opacity: 0.5;
}
.graph .nodeContainer .graphAction.push {
background: rgba(61, 139, 255, 0.9);
}
.graph .nodeContainer .graphAction.pull {
background: rgba(38, 189, 189, 0.9);
}
.graph .nodeContainer .graphAction.reset {
background: rgba(255, 129, 31, 0.9);
}
.graph .nodeContainer .graphAction.rebase {
background: rgba(65, 222, 60, 0.9);
}
.graph .nodeContainer .graphAction.move {
width: auto;
background: rgba(0, 0, 0, 0.1);
}
.graph .nodeContainer .graphAction.merge {
background: rgba(208, 135, 212, 0.9);
}
.graph .nodeContainer .graphAction.checkout {
background: rgba(205, 219, 55, 0.9);
}
.graph .nodeContainer .graphAction.delete {
background: rgba(214, 77, 56, 0.9);
}
.graph .nodeContainer .graphAction.cherry-pick {
background: rgba(110, 156, 110, 0.9);
}
.graph .nodeContainer .graphAction.uncommit {
background: rgba(158, 53, 20, 0.9);
}
.graph .nodeContainer .graphAction.revert {
background: rgba(179, 135, 43, 0.9);
}
.graph .nodeContainer .newRef {
opacity: 1;
padding-top: 2px;
padding-bottom: 2px;
margin-left: 5px;
}
.graph .nodeContainer .newRef .showBranchingForm {
background: transparent;
border: 0px;
cursor: pointer;
padding: 0px;
margin: 0px;
margin-top: 9px;
color: rgba(255, 255, 255, 0.3);
}
.graph .nodeContainer .newRef .showBranchingForm:hover {
color: rgba(255, 255, 255, 0.8);
}
.graph .nodeContainer .newRef .form-inline {
display: inline-block;
}
.graph .nodeContainer .newRef input.name {
width: 150px;
}
.graph .graphFooter {
height: 60px;
}
.graph .graphFooter .progressBar {
position: relative;
top: -150px;
margin-left: 400px;
}
.graph .remote-icon {
background: url('images/remoteIcon.png');
width: 12px;
height: 10px;
display: inline-block;
}
.graph .branch-icon {
background: url('images/branchIcon.png');
width: 9px;
height: 10px;
display: inline-block;
}
.graph .tag-icon {
background: url('images/tagIcon.png');
width: 5px;
height: 10px;
display: inline-block;
}

View File

@ -1,65 +0,0 @@
<div class="graph" data-bind="scrolledToEnd: scrolledToEnd, click: handleBubbledClick" data-ta-clickable="graph">
<!-- ko template: { name: 'graphGraphics' } --><!-- /ko -->
<div class="nodes" data-bind="foreach: nodes">
<div class="nodeContainer animation" data-ta-container="node" data-bind="style: { left: '0px', top: cy() + 'px' }, attr: { 'data-ta-node-title': title }">
<div class="commit-container animation" data-bind="visible: commitContainerVisible, style: { left: cx() - 620 + 'px' }">
<!-- ko component: commitComponent -->
<!-- /ko -->
</div>
<div class="rightSideContainer" data-bind="style: { left: cx() + r() - 433 + 'px' }">
<!-- ko foreach: branches -->
<span class="ref branch" data-ta-clickable="branch" draggable="true" tabIndex="-1"
data-bind="css: { current: current, remote: isRemoteBranch, dragging: isDragging, focused: selected },
click: selected,
dragStart: dragStart, dragEnd: dragEnd, attr: { 'data-ta-name': localRefName, 'data-ta-current': current, 'data-ta-local': isLocal }" >
<span class="octicon octicon-broadcast" data-bind="visible: isRemoteBranch"></span>
<span class="octicon octicon-git-branch"></span>
<span class="name" data-bind="text: localRefName"></span>
</span>
<!-- /ko -->
<!-- ko foreach: tags -->
<span class="ref tag" data-ta-clickable="tag" draggable="true" tabIndex="0"
data-bind="css: { current: current, remote: isRemoteTag, dragging: isDragging, focused: selected },
click: selected,
dragStart: dragStart, dragEnd: dragEnd, attr: { 'data-ta-name': localRefName, 'data-ta-current': current }">
<span class="octicon octicon-globe" data-bind="visible: isRemote"></span>
<span class="octicon octicon-tag"></span>
<span class="name" data-bind="text: localRefName"></span>
</span>
<!-- /ko -->
<!-- ko foreach: dropareaGraphActions -->
<span class="graphAction droparea" data-bind="css: cssClasses, visible: visible, attr: { 'data-ta-action': style, 'data-ta-visible': visible }, event: { mouseover: mouseover, mouseout: mouseout }">
<!-- ko if: icon --><span data-bind="css: icon" class="icon"></span><!-- /ko -->
<span data-bind="text: text"></span>
<div class="dropmask" tabIndex="0" role="button" data-bind="dropOver: visible, drop: doPerform, dragEnter: dragEnter, dragLeave: dragLeave, click: doPerform"></div>
<!-- ko component: performProgressBar --><!-- /ko -->
</span>
<!-- /ko -->
<!-- ko if: showNewRefAction -->
<span class="newRef" data-bind="css: { editing: branchingFormVisible }">
<button class="showBranchingForm" data-ta-clickable="show-new-branch-form" data-bind="click: showBranchingForm, visible: !branchingFormVisible()">&#x271A;</button>
<!-- ko if: branchingFormVisible -->
<div class="form-inline">
<input class="name form-control" data-ta-input="new-branch-name" type="text" data-bind="value: newBranchName, hasfocus: newBranchNameHasFocus, valueUpdate: 'afterkeydown'"/>
<button class="btn btn-primary" data-ta-clickable="create-branch" type="submit" value="Branch" data-bind="click: createBranch, enable: canCreateRef">Branch</button>
<button class="btn btn-default" data-ta-clickable="create-tag" data-bind="click: createTag, enable: canCreateRef">Tag</button>
</div>
<!-- /ko -->
</span>
<!-- /ko -->
</div>
</div>
</div>
<div class="graphFooter">
<!-- ko component: nodesLoader --><!-- /ko -->
</div>
</div>

View File

@ -1,297 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var GitNodeViewModel = require('./git-node');
var GitRefViewModel = require('./git-ref');
var _ = require('lodash');
var moment = require('moment');
var EdgeViewModel = require('./edge');
components.register('graph', function(args) {
return new GraphViewModel(args.server, args.repoPath);
});
function GraphViewModel(server, repoPath) {
var self = this;
this.repoPath = repoPath;
this.maxNNodes = 25;
this.server = server;
this.currentRemote = ko.observable();
this.nodesLoader = components.create('progressBar', {
predictionMemoryKey: 'gitgraph-' + self.repoPath(),
fallbackPredictedTimeMs: 1000,
temporary: true
});
this.nodes = ko.observableArray();
this.edges = ko.observableArray();
this.refs = ko.observableArray();
this.nodesById = {};
this.refsByRefName = {};
this.checkedOutBranch = ko.observable();
this.checkedOutRef = ko.computed(function() {
return self.checkedOutBranch() ? self.getRef('refs/heads/' + self.checkedOutBranch()) : null;
});
this.HEADref = ko.observable();
this.HEAD = ko.computed(function() {
return self.HEADref() ? self.HEADref().node() : undefined;
});
this.commitNodeColor = ko.computed(function() {
return self.HEAD() ? self.HEAD().color() : '#4A4A4A';
});
this.commitNodeEdge = ko.computed(function() {
if (!self.HEAD() || !self.HEAD().cx() || !self.HEAD().cy()) return;
return "M 610 68 L " + self.HEAD().cx() + " " + self.HEAD().cy();
});
this.showCommitNode = ko.observable(false);
this.currentActionContext = ko.observable();
this.edgesById = {};
this.scrolledToEnd = _.debounce(function() {
self.maxNNodes = self.maxNNodes + 25;
self.loadNodesFromApi();
}, 500, true);
this.dimCommit = ko.observable(false);
this.commitOpacity = ko.computed(function() { return self.dimCommit() ? 0.1 : 1; });
this.heighstBranchOrder = 0;
this.hoverGraphActionGraphic = ko.observable();
this.hoverGraphActionGraphic.subscribe(function(value) {
if (value && value.destroy)
value.destroy();
}, null, 'beforeChange');
this.hoverGraphAction = ko.observable();
this.hoverGraphAction.subscribe(function(value) {
if (value && value.createHoverGraphic) {
self.hoverGraphActionGraphic(value.createHoverGraphic());
} else {
self.hoverGraphActionGraphic(null);
}
});
this.loadNodesFromApiThrottled = _.throttle(this.loadNodesFromApi.bind(this), 500);
this.updateBranchesThrottled = _.throttle(this.updateBranches.bind(this), 500);
this.loadNodesFromApiThrottled();
this.updateBranchesThrottled();
this.graphWidth = ko.observable();
this.graphHeight = ko.observable();
}
GraphViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('graph', this, {}, parentElement);
}
GraphViewModel.prototype.getNode = function(sha1, logEntry) {
var nodeViewModel = this.nodesById[sha1];
if (!nodeViewModel) nodeViewModel = this.nodesById[sha1] = new GitNodeViewModel(this, sha1);
if (logEntry) nodeViewModel.setData(logEntry);
return nodeViewModel;
}
GraphViewModel.prototype.getRef = function(ref, constructIfUnavailable) {
if (constructIfUnavailable === undefined) constructIfUnavailable = true;
var refViewModel = this.refsByRefName[ref];
if (!refViewModel && constructIfUnavailable) {
refViewModel = this.refsByRefName[ref] = new GitRefViewModel(ref, this);
this.refs.push(refViewModel);
if (refViewModel.name === 'HEAD') {
this.HEADref(refViewModel);
}
}
return refViewModel;
}
GraphViewModel.prototype.loadNodesFromApi = function(callback) {
var self = this;
this.nodesLoader.start();
this.server.getPromise('/log', { path: this.repoPath(), limit: this.maxNNodes })
.then(function(nodes) {
nodes = self.computeNode(nodes.map(function(logEntry) {
return self.getNode(logEntry.sha1, logEntry);
}));
var edges = [];
nodes.forEach(function(node) {
node.parents().forEach(function(parentSha1) {
edges.push(self.getEdge(node.sha1, parentSha1));
});
node.render();
});
self.edges(edges);
self.nodes(nodes);
if (nodes.length > 0) {
self.graphHeight(nodes[nodes.length - 1].cy() + 80);
}
self.graphWidth(1000 + (self.heighstBranchOrder * 90));
}).finally(function() {
self.nodesLoader.stop();
if (callback) callback();
});
}
GraphViewModel.prototype.traverseNodeLeftParents = function(node, callback) {
callback(node);
var parent = this.nodesById[node.parents()[0]];
if (parent) {
this.traverseNodeLeftParents(parent, callback);
}
}
GraphViewModel.prototype.computeNode = function(nodes) {
var self = this;
if (!nodes) {
nodes = this.nodes();
}
this.markNodesIdeologicalBranches(this.refs(), nodes, this.nodesById);
var updateTimeStamp = moment().valueOf();
if (this.HEAD()) {
this.traverseNodeLeftParents(this.HEAD(), function(node) {
node.ancestorOfHEADTimeStamp = updateTimeStamp;
});
}
// Filter out nodes which doesn't have a branch (staging and orphaned nodes)
nodes = nodes.filter(function(node) { return (node.ideologicalBranch() && !node.ideologicalBranch().isStash) || node.ancestorOfHEADTimeStamp == updateTimeStamp; })
var branchSlots = [];
// Then iterate from the bottom to fix the orders of the branches
for (var i = nodes.length - 1; i >= 0; i--) {
var node = nodes[i];
if (node.ancestorOfHEADTimeStamp == updateTimeStamp) continue;
var ideologicalBranch = node.ideologicalBranch();
// First occurence of the branch, find an empty slot for the branch
if (ideologicalBranch.lastSlottedTimeStamp != updateTimeStamp) {
ideologicalBranch.lastSlottedTimeStamp = updateTimeStamp;
var slot = branchSlots.indexOf(undefined);
if (slot === -1) {
branchSlots.push(ideologicalBranch);
slot = branchSlots.length - 1;
}
ideologicalBranch.branchOrder = slot;
branchSlots[slot] = slot;
}
node.branchOrder(ideologicalBranch.branchOrder);
self.heighstBranchOrder = Math.max(self.heighstBranchOrder, node.branchOrder());
}
var prevNode;
nodes.forEach(function(node) {
node.branchOrder(branchSlots.length - node.branchOrder());
node.ancestorOfHEAD(node.ancestorOfHEADTimeStamp == updateTimeStamp);
node.aboveNode = prevNode;
if (prevNode) prevNode.belowNode = node;
prevNode = node;
});
return nodes;
}
GraphViewModel.prototype.getEdge = function(nodeAsha1, nodeBsha1) {
var id = nodeAsha1 + '-' + nodeBsha1;
var edge = this.edgesById[id];
if (!edge) {
edge = this.edgesById[id] = new EdgeViewModel(this, nodeAsha1, nodeBsha1);
}
return edge;
}
GraphViewModel._markIdeologicalStamp = 0;
GraphViewModel.prototype.markNodesIdeologicalBranches = function(refs, nodes, nodesById) {
var self = this;
refs = refs.filter(function(r) { return !!r.node(); });
refs = refs.sort(function(a, b) {
if (a.isLocal && !b.isLocal) return -1;
if (b.isLocal && !a.isLocal) return 1;
if (a.isBranch && !b.isBranch) return -1;
if (b.isBranch && !a.isBranch) return 1;
if (a.isHEAD && !b.isHEAD) return 1;
if (!a.isHEAD && b.isHEAD) return -1;
if (a.isStash && !b.isStash) return 1;
if (b.isStash && !a.isStash) return -1;
if (a.node() && a.node().date && b.node() && b.node().date)
return b.node().date - a.node().date;
return a.refName < b.refName ? -1 : 1;
});
var stamp = GraphViewModel._markIdeologicalStamp++;
refs.forEach(function(ref) {
self.traverseNodeParents(ref.node(), function(node) {
if (node.stamp == stamp) return false;
node.stamp = stamp;
node.ideologicalBranch(ref);
return true;
});
});
}
GraphViewModel.prototype.traverseNodeParents = function(node, callback) {
if (!callback(node)) return false;
for (var i = 0; i < node.parents().length; i++) {
// if parent, travers parent
var parent = this.nodesById[node.parents()[i]];
if (parent) {
this.traverseNodeParents(parent, callback);
}
}
}
GraphViewModel.prototype.handleBubbledClick = function(elem, event) {
// If the clicked element is bound to the current action context,
// then let's not deselect it.
if (ko.dataFor(event.target) === this.currentActionContext()) return;
if (this.currentActionContext() && this.currentActionContext() instanceof GitNodeViewModel) {
this.currentActionContext().toggleSelected();
} else {
this.currentActionContext(null);
}
// If the click was on an input element, then let's allow the default action to proceed.
// This is especially needed since for some strange reason any submit (ie. enter in a textbox)
// will trigger a click event on the submit input of the form, which will end up here,
// and if we don't return true, then the submit event is never fired, breaking stuff.
if (event.target.nodeName === 'INPUT') return true;
}
GraphViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'git-directory-changed') {
this.loadNodesFromApiThrottled();
this.updateBranchesThrottled();
} else if (event.event == 'request-app-content-refresh') {
this.loadNodesFromApiThrottled();
} else if (event.event == 'remote-tags-update') {
this.setRemoteTags(event.tags);
} else if (event.event == 'current-remote-changed') {
this.currentRemote(event.newRemote);
} else if (event.event == 'graph-render') {
this.nodes().forEach(function(node) {
node.render();
});
}
}
GraphViewModel.prototype.updateBranches = function() {
var self = this;
this.server.get('/checkout', { path: this.repoPath() }, function(err, branch) {
if (err && err.errorCode == 'not-a-repository') return true;
if (err) return;
self.checkedOutBranch(branch);
});
}
GraphViewModel.prototype.setRemoteTags = function(remoteTags) {
var self = this;
var nodeIdsToRemoteTags = {};
remoteTags.forEach(function(ref) {
if (ref.name.indexOf('^{}') != -1) {
var tagRef = ref.name.slice(0, ref.name.length - '^{}'.length);
var name = 'remote-tag: ' + ref.remote + '/' + tagRef.split('/')[2];
self.getRef(name).node(self.getNode(ref.sha1));
}
});
}
GraphViewModel.prototype.checkHeadMove = function(toNode) {
if (this.HEAD() === toNode) {
this.HEADref.node(toNode);
}
}

View File

@ -1,197 +0,0 @@
@import "public/less/variables.less";
.graph {
position: relative;
display: inline-block;
width: 100%;
.graphLog {
left: 575px;
}
.nodeContainer {
position: absolute;
left: 30px;
top: 120px;
width: 100%;
.commit-container {
position: absolute;
top: -43px;
}
.rightSideContainer {
position: absolute;
display: flex;
margin-left: (@log-width-small + 40px);
top: -22px;
white-space: nowrap;
height: 40px;
}
.droparea {
margin: 8px 0px 4px 0px;
}
.ref {
display: inline-block;
opacity: 0.6;
cursor: move;
margin: 7px 0px 4px 0px;
outline: none;
padding: 2px 5px 2px 5px;
border: 3px solid transparent;
border-radius: 10px;
transition: border-color 0.5s;
-webkit-transition: border-color 0.5s;
&.dragging.focused {
border-color: transparent;
}
&.focused {
border-color: #fff;
}
&.current {
font-weight: bold;
opacity: 1;
font-size: 20px;
margin-top: 2px;
margin-bottom: -2px;
.octicon {
font-size: 20px;
}
}
&.remote {
color: #5DB4FF;
}
&.tag {
color: #EEF266;
}
}
.graphAction {
color: #fff;
cursor: pointer;
opacity: 1;
transition: all 0.5s ease 0.2s;
-webkit-transition: all 0.5s ease 0.2s;
transition-property: opacity, max-width;
-webkit-transition-property: opacity, max-width;
margin-right: 2.5px;
margin-left: 2.5px;
overflow: hidden;
.icon {
&.flip {
-webkit-transform:rotate(-180deg);
-moz-transform:rotate(-180deg);
-o-transform:rotate(-180deg);
transform:rotate(-180deg);
}
&.octicon-git-merge,&.octicon-repo-forked, &.octicon-zap {
margin-left: 3px;
}
&.octicon-history {
margin-left: 2px;
}
&.glyphicon-remove,&.octicon-circuit-board {
margin-left: 1px;
}
margin-right: 6px;
}
&.droparea {
padding-left: 7px;
}
&.dimmed {
opacity: 0.5;
}
&.push {
background: rgba(61, 139, 255, 0.9);
}
&.pull {
background: rgba(38, 189, 189, 0.9);
}
&.reset {
background: rgba(255, 129, 31, 0.9);
}
&.rebase {
background: rgba(65, 222, 60, 0.9);
}
&.move {
width: auto;
background: rgba(0, 0, 0, 0.1);
}
&.merge {
background: rgba(208, 135, 212, 0.9);
}
&.checkout {
background: rgba(205, 219, 55, 0.9);
}
&.delete {
background: rgba(214, 77, 56, 0.9);
}
&.cherry-pick {
background: rgba(110, 156, 110, 0.9);
}
&.uncommit {
background: rgba(158, 53, 20, 0.9);
}
&.revert {
background: rgba(179, 135, 43, 0.9);
}
}
.newRef {
opacity: 1;
padding-top: 2px;
padding-bottom: 2px;
margin-left: 5px;
.showBranchingForm {
background: transparent;
border: 0px;
cursor: pointer;
padding: 0px;
margin: 0px;
margin-top: 9px;
color: rgba(255, 255, 255, 0.3);
&:hover {
color: rgba(255, 255, 255, 0.8);
}
}
.form-inline {
display: inline-block;
}
input.name {
width: 150px;
}
}
}
.graphFooter {
height: 60px;
.progressBar {
position: relative;
top: -150px;
margin-left: (@log-width-small);
}
}
.remote-icon {
background: url('images/remoteIcon.png');
width: 12px;
height: 10px;
display: inline-block;
}
.branch-icon {
background: url('images/branchIcon.png');
width: 9px;
height: 10px;
display: inline-block;
}
.tag-icon {
background: url('images/tagIcon.png');
width: 5px;
height: 10px;
display: inline-block;
}
}

View File

@ -1,77 +0,0 @@
var getEdgeModelWithD = function(d, stroke, strokeWidth, strokeDasharray, markerEnd) {
return { d: d,
stroke: stroke ? stroke : '#4A4A4A',
strokeWidth: strokeWidth ? strokeWidth : '8',
strokeDasharray: strokeDasharray ? strokeDasharray : '10, 5',
markerEnd: markerEnd ? markerEnd : '' };
}
var getEdgeModel = function(scx, scy, tcx, tcy, stroke, strokeWidth, strokeDasharray, markerEnd) {
return getEdgeModelWithD("M " + scx + " " + scy + " L " + tcx + " " + tcy, stroke, strokeWidth, strokeDasharray, markerEnd);
}
var getNodeModel = function(cx, cy, r, fill, stroke, strokeWidth, strokeDasharray) {
return { cx: cx,
cy: cy,
r: r,
fill: fill,
stroke: stroke ? stroke : '#41DE3C',
strokeWidth: strokeWidth ? strokeWidth : '8',
strokeDasharray: strokeDasharray ? strokeDasharray : '10, 5' };
}
function HoverViewModel() {
this.bgEdges = [];
this.nodes = [];
this.fgEdges = [];
}
function MergeViewModel(graph, headNode, node) {
var self = this;
HoverViewModel.call(this);
this.graph = graph;
this.bgEdges = [ getEdgeModel(headNode.cx(), (headNode.cy() - 110), headNode.cx(), headNode.cy()),
getEdgeModel(headNode.cx(), (headNode.cy() - 110), node.cx(), node.cy()) ];
this.nodes = [ getNodeModel(headNode.cx(), headNode.cy() - 110, Math.max(headNode.r(), node.r()), '#252833', '#41DE3C', '8', '10, 5') ];
graph.dimCommit(true);
}
exports.MergeViewModel = MergeViewModel;
MergeViewModel.prototype.destroy = function() {
this.graph.dimCommit(false);
}
function RebaseViewModel(onto, nodesThatWillMove) {
var self = this;
HoverViewModel.call(this);
nodesThatWillMove = nodesThatWillMove.slice(0, -1);
if (nodesThatWillMove.length == 0) return;
this.bgEdges.push(getEdgeModel(onto.cx(), onto.cy(), onto.cx(), onto.cy() - 60));
nodesThatWillMove.forEach(function(node, i) {
var cy = onto.cy() + (-90 * (i + 1));
self.nodes.push(getNodeModel(onto.cx(), cy, 28, 'transparent'));
if (i + 1 < nodesThatWillMove.length) {
self.bgEdges.push(getEdgeModel(onto.cx(), (cy - 25), onto.cx(), (cy - 65)));
}
});
}
exports.RebaseViewModel = RebaseViewModel;
function ResetViewModel(nodes) {
var self = this;
HoverViewModel.call(this);
nodes.forEach(function(node) {
self.fgEdges.push(getEdgeModelWithD(node.getLeftToRightStrike(), 'rgb(255, 129, 31)', '8', '0, 0'))
self.fgEdges.push(getEdgeModelWithD(node.getRightToLeftStrike(), 'rgb(255, 129, 31)', '8', '0, 0'));
});
}
exports.ResetViewModel = ResetViewModel;
function PushViewModel(fromNode, toNode) {
HoverViewModel.call(this);
this.fgEdges = [getEdgeModel(fromNode.cx(), fromNode.cy(), toNode.cx(), (toNode.cy() + 40), 'rgb(61, 139, 255)', '15', '10, 5', 'url(#pushArrowEnd)' )];
}
exports.PushViewModel = PushViewModel;

View File

@ -1,19 +0,0 @@
var ko = require('knockout');
var Selectable = function(graph) {
this.selected = ko.computed({
read: function() {
return graph.currentActionContext() == this;
},
write: function(val) {
// val is this if we're called from a click ko binding
if (val === this || val === true) {
graph.currentActionContext(this);
} else if (graph.currentActionContext() == this) {
graph.currentActionContext(null);
}
},
owner: this
});
};
module.exports = Selectable;

View File

@ -1,10 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"graph": "graph.html",
"graphGraphics": "graph-graphics.html"
},
"javascript": "graph.bundle.js",
"css": "graph.css"
}
}

View File

@ -1,43 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
var navigation = require('ungit-navigation');
var programEvents = require('ungit-program-events');
components.register('header', function(args) {
return new HeaderViewModel(args.app);
});
function HeaderViewModel(app) {
var self = this;
this.app = app;
this.showBackButton = ko.observable(false);
this.path = ko.observable();
this.currentVersion = ungit.version;
this.refreshButton = components.create('refreshbutton');
this.showAddToRepoListButton = ko.computed(function() {
return self.path() && self.app.repoList().indexOf(self.path()) == -1;
});
}
HeaderViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('header', this, {}, parentElement);
}
HeaderViewModel.prototype.submitPath = function() {
navigation.browseTo('repository?path=' + encodeURIComponent(this.path()));
}
HeaderViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'navigation-changed') {
this.showBackButton(event.path != '');
if (event.path == '') this.path('');
} else if (event.event == 'navigated-to-path') {
this.path(event.path);
}
}
HeaderViewModel.prototype.addCurrentPathToRepoList = function() {
programEvents.dispatch({ event: 'request-remember-repo', repoPath: this.path() });
return true;
}
},{"knockout":"knockout","ungit-components":"ungit-components","ungit-navigation":"ungit-navigation","ungit-program-events":"ungit-program-events"}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL2hlYWRlci9oZWFkZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJcbnZhciBrbyA9IHJlcXVpcmUoJ2tub2Nrb3V0Jyk7XG52YXIgY29tcG9uZW50cyA9IHJlcXVpcmUoJ3VuZ2l0LWNvbXBvbmVudHMnKTtcbnZhciBuYXZpZ2F0aW9uID0gcmVxdWlyZSgndW5naXQtbmF2aWdhdGlvbicpO1xudmFyIHByb2dyYW1FdmVudHMgPSByZXF1aXJlKCd1bmdpdC1wcm9ncmFtLWV2ZW50cycpO1xuXG5jb21wb25lbnRzLnJlZ2lzdGVyKCdoZWFkZXInLCBmdW5jdGlvbihhcmdzKSB7XG4gIHJldHVybiBuZXcgSGVhZGVyVmlld01vZGVsKGFyZ3MuYXBwKTtcbn0pO1xuXG5mdW5jdGlvbiBIZWFkZXJWaWV3TW9kZWwoYXBwKSB7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgdGhpcy5hcHAgPSBhcHA7XG4gIHRoaXMuc2hvd0JhY2tCdXR0b24gPSBrby5vYnNlcnZhYmxlKGZhbHNlKTtcbiAgdGhpcy5wYXRoID0ga28ub2JzZXJ2YWJsZSgpO1xuICB0aGlzLmN1cnJlbnRWZXJzaW9uID0gdW5naXQudmVyc2lvbjtcbiAgdGhpcy5yZWZyZXNoQnV0dG9uID0gY29tcG9uZW50cy5jcmVhdGUoJ3JlZnJlc2hidXR0b24nKTtcbiAgdGhpcy5zaG93QWRkVG9SZXBvTGlzdEJ1dHRvbiA9IGtvLmNvbXB1dGVkKGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBzZWxmLnBhdGgoKSAmJiBzZWxmLmFwcC5yZXBvTGlzdCgpLmluZGV4T2Yoc2VsZi5wYXRoKCkpID09IC0xO1xuICB9KTtcbn1cbkhlYWRlclZpZXdNb2RlbC5wcm90b3R5cGUudXBkYXRlTm9kZSA9IGZ1bmN0aW9uKHBhcmVudEVsZW1lbnQpIHtcbiAga28ucmVuZGVyVGVtcGxhdGUoJ2hlYWRlcicsIHRoaXMsIHt9LCBwYXJlbnRFbGVtZW50KTtcbn1cbkhlYWRlclZpZXdNb2RlbC5wcm90b3R5cGUuc3VibWl0UGF0aCA9IGZ1bmN0aW9uKCkge1xuICBuYXZpZ2F0aW9uLmJyb3dzZVRvKCdyZXBvc2l0b3J5P3BhdGg9JyArIGVuY29kZVVSSUNvbXBvbmVudCh0aGlzLnBhdGgoKSkpO1xufVxuSGVhZGVyVmlld01vZGVsLnByb3RvdHlwZS5vblByb2dyYW1FdmVudCA9IGZ1bmN0aW9uKGV2ZW50KSB7XG4gIGlmIChldmVudC5ldmVudCA9PSAnbmF2aWdhdGlvbi1jaGFuZ2VkJykge1xuICAgIHRoaXMuc2hvd0JhY2tCdXR0b24oZXZlbnQucGF0aCAhPSAnJyk7XG4gICAgaWYgKGV2ZW50LnBhdGggPT0gJycpIHRoaXMucGF0aCgnJyk7XG4gIH0gZWxzZSBpZiAoZXZlbnQuZXZlbnQgPT0gJ25hdmlnYXRlZC10by1wYXRoJykge1xuICAgIHRoaXMucGF0aChldmVudC5wYXRoKTtcbiAgfVxufVxuSGVhZGVyVmlld01vZGVsLnByb3RvdHlwZS5hZGRDdXJyZW50UGF0aFRvUmVwb0xpc3QgPSBmdW5jdGlvbigpIHtcbiAgcHJvZ3JhbUV2ZW50cy5kaXNwYXRjaCh7IGV2ZW50OiAncmVxdWVzdC1yZW1lbWJlci1yZXBvJywgcmVwb1BhdGg6IHRoaXMucGF0aCgpIH0pO1xuICByZXR1cm4gdHJ1ZTtcbn1cbiJdfQ==

View File

@ -1,72 +0,0 @@
.navbarPadder {
height: 81px;
}
.navbar {
padding-top: 13px;
z-index: 5;
box-shadow: 0px 15px 15px #252833;
height: 81px;
}
.navbar .backlink {
margin-left: 9px;
margin-top: 7px;
position: absolute;
color: #686868;
}
.navbar .backlink:hover {
text-decoration: none;
color: #A5A5A5;
}
.navbar .backlink .glyphicon-arrow-left {
-webkit-transition: opacity 0.5s ease-in-out;
-moz-transition: opacity 0.5s ease-in-out;
transition: opacity 0.5s ease-in-out;
opacity: 0;
}
.navbar .backlink .glyphicon-arrow-left.glyph-shown {
opacity: 1;
}
.navbar .form-container {
margin-left: 180px;
margin-right: 107px;
}
.navbar .form-container .path-input-form {
width: 100%;
position: relative;
}
.navbar .form-container .path-input-form .form-control {
font-size: 1.7em;
width: 100%;
}
.navbar .form-container .path-input-form .add-to-repolist {
position: absolute;
right: 4px;
top: 6px;
background: transparent;
border: 0px;
opacity: 0.3;
}
.navbar .form-container .path-input-form .add-to-repolist:hover {
opacity: 0.8;
}
.navbar .arrowUp {
position: absolute;
bottom: 0px;
left: 300px;
height: 0px;
width: 0px;
border-bottom: 15px solid #242832;
border-right: 15px solid transparent;
border-left: 15px solid transparent;
}
.navbar .version {
position: absolute;
right: 5px;
bottom: 2px;
font-size: 12px;
}
.toolbar {
position: absolute;
right: 55px;
top: 13px;
}

View File

@ -1,25 +0,0 @@
<div class="navbar navbar-default navbar-fixed-top">
<a data-ta-clickable="home-link" class="backlink bootstrap-tooltip" href="#/" data-toggle="tooltip" data-placement="bottom" data-original-title="Navigate to Ungit home page" data-delay='{"show":"2000", "hide":"0"}'>
<span class="glyphicon glyphicon-arrow-left glyphicon-circled" data-bind="css: { 'glyph-shown': showBackButton }"></span>
<img src="images/logo.png">
</a>
<div class="form-container">
<form class="path-input-form" data-bind="submit: submitPath">
<input data-ta-input="navigation-path" type="text" class="form-control input-lg" data-bind="value: path, autocomplete: path" placeholder="Enter path to repository">
<button type="button" class="add-to-repolist btn btn-default bootstrap-tooltip" data-bind="visible: showAddToRepoListButton, click: addCurrentPathToRepoList" data-toggle="tooltip" data-placement="bottom" data-original-title="Add current git directory to Ungit home page" data-delay='{"show":"2000", "hide":"0"}'>
<span class="glyphicon glyphicon-plus"></span>
</button>
</form>
</div>
<div class="toolbar">
<!-- ko component: refreshButton --><!-- /ko -->
</div>
<div class="arrowUp"></div>
<div class="version" data-bind="text: currentVersion"></div>
</div>
<div class="navbarPadder"></div>

View File

@ -1,39 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var navigation = require('ungit-navigation');
var programEvents = require('ungit-program-events');
components.register('header', function(args) {
return new HeaderViewModel(args.app);
});
function HeaderViewModel(app) {
var self = this;
this.app = app;
this.showBackButton = ko.observable(false);
this.path = ko.observable();
this.currentVersion = ungit.version;
this.refreshButton = components.create('refreshbutton');
this.showAddToRepoListButton = ko.computed(function() {
return self.path() && self.app.repoList().indexOf(self.path()) == -1;
});
}
HeaderViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('header', this, {}, parentElement);
}
HeaderViewModel.prototype.submitPath = function() {
navigation.browseTo('repository?path=' + encodeURIComponent(this.path()));
}
HeaderViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'navigation-changed') {
this.showBackButton(event.path != '');
if (event.path == '') this.path('');
} else if (event.event == 'navigated-to-path') {
this.path(event.path);
}
}
HeaderViewModel.prototype.addCurrentPathToRepoList = function() {
programEvents.dispatch({ event: 'request-remember-repo', repoPath: this.path() });
return true;
}

View File

@ -1,75 +0,0 @@
.navbarPadder {
height: 81px;
}
.navbar {
padding-top: 13px;
z-index: 5;
box-shadow: 0px 15px 15px #252833;
height: 81px;
.backlink {
margin-left: 9px;
margin-top: 7px;
position: absolute;
color: #686868;
&:hover {
text-decoration: none;
color: #A5A5A5;
}
.glyphicon-arrow-left {
-webkit-transition: opacity 0.5s ease-in-out;
-moz-transition: opacity 0.5s ease-in-out;
transition: opacity 0.5s ease-in-out;
opacity: 0;
&.glyph-shown {
opacity: 1;
}
}
}
.form-container {
margin-left: 180px;
margin-right: 107px;
.path-input-form {
width: 100%;
position: relative;
.form-control {
font-size: 1.7em;
width: 100%;
}
.add-to-repolist {
position: absolute;
right: 4px;
top: 6px;
background: transparent;
border: 0px;
opacity: 0.3;
&:hover {
opacity: 0.8;
}
}
}
}
.arrowUp {
position: absolute;
bottom: 0px;
left: 300px;
height: 0px;
width: 0px;
border-bottom: 15px solid #242832;
border-right: 15px solid transparent;
border-left: 15px solid transparent;
}
.version {
position: absolute;
right: 5px;
bottom: 2px;
font-size: 12px;
}
}
.toolbar {
position: absolute;
right: 55px;
top: 13px;
}

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"header": "header.html"
},
"javascript": "header.bundle.js",
"css": "header.css"
}
}

View File

@ -1,66 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
components.register('home', function(args) {
return new HomeViewModel(args.app);
});
function HomeRepositoryViewModel(home, path) {
this.home = home;
this.app = home.app;
this.server = this.app.server;
this.path = path;
this.title = path;
this.link = '/#/repository?path=' + encodeURIComponent(path);
this.pathRemoved = ko.observable(false);
this.remote = ko.observable('...');
this.updateState();
}
HomeRepositoryViewModel.prototype.updateState = function() {
var self = this;
this.server.get('/fs/exists?path=' + encodeURIComponent(this.path), undefined, function(err, exists) {
self.pathRemoved(!exists);
});
this.server.get('/remotes/origin?path=' + encodeURIComponent(this.path), undefined, function(err, remote) {
if (err) {
self.remote('');
return true;
}
self.remote(remote.address);
});
}
HomeRepositoryViewModel.prototype.remove = function() {
this.app.repoList.remove(this.path);
this.home.update();
}
function HomeViewModel(app) {
var self = this;
this.app = app;
this.repos = ko.observableArray();
this.showNux = ko.computed(function() {
return self.repos().length == 0;
});
}
HomeViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('home', this, {}, parentElement);
}
HomeViewModel.prototype.template = 'home';
HomeViewModel.prototype.shown = function() {
this.update();
}
HomeViewModel.prototype.update = function() {
var self = this;
var reposByPath = {};
this.repos().forEach(function(repo) { reposByPath[repo.path] = repo; });
this.repos(this.app.repoList().sort().map(function(path) {
if (!reposByPath[path])
reposByPath[path] = new HomeRepositoryViewModel(self, path);
return reposByPath[path];
}));
}
},{"knockout":"knockout","ungit-components":"ungit-components"}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL2hvbWUvaG9tZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJcbnZhciBrbyA9IHJlcXVpcmUoJ2tub2Nrb3V0Jyk7XG52YXIgY29tcG9uZW50cyA9IHJlcXVpcmUoJ3VuZ2l0LWNvbXBvbmVudHMnKTtcblxuY29tcG9uZW50cy5yZWdpc3RlcignaG9tZScsIGZ1bmN0aW9uKGFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBIb21lVmlld01vZGVsKGFyZ3MuYXBwKTtcbn0pO1xuXG5mdW5jdGlvbiBIb21lUmVwb3NpdG9yeVZpZXdNb2RlbChob21lLCBwYXRoKSB7XG4gIHRoaXMuaG9tZSA9IGhvbWU7XG4gIHRoaXMuYXBwID0gaG9tZS5hcHA7XG4gIHRoaXMuc2VydmVyID0gdGhpcy5hcHAuc2VydmVyO1xuICB0aGlzLnBhdGggPSBwYXRoO1xuICB0aGlzLnRpdGxlID0gcGF0aDtcbiAgdGhpcy5saW5rID0gJy8jL3JlcG9zaXRvcnk/cGF0aD0nICsgZW5jb2RlVVJJQ29tcG9uZW50KHBhdGgpO1xuICB0aGlzLnBhdGhSZW1vdmVkID0ga28ub2JzZXJ2YWJsZShmYWxzZSk7XG4gIHRoaXMucmVtb3RlID0ga28ub2JzZXJ2YWJsZSgnLi4uJyk7XG4gIHRoaXMudXBkYXRlU3RhdGUoKTtcbn1cbkhvbWVSZXBvc2l0b3J5Vmlld01vZGVsLnByb3RvdHlwZS51cGRhdGVTdGF0ZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHRoaXMuc2VydmVyLmdldCgnL2ZzL2V4aXN0cz9wYXRoPScgKyBlbmNvZGVVUklDb21wb25lbnQodGhpcy5wYXRoKSwgdW5kZWZpbmVkLCBmdW5jdGlvbihlcnIsIGV4aXN0cykge1xuICAgIHNlbGYucGF0aFJlbW92ZWQoIWV4aXN0cyk7XG4gIH0pO1xuICB0aGlzLnNlcnZlci5nZXQoJy9yZW1vdGVzL29yaWdpbj9wYXRoPScgKyBlbmNvZGVVUklDb21wb25lbnQodGhpcy5wYXRoKSwgdW5kZWZpbmVkLCBmdW5jdGlvbihlcnIsIHJlbW90ZSkge1xuICAgIGlmIChlcnIpIHtcbiAgICAgIHNlbGYucmVtb3RlKCcnKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBzZWxmLnJlbW90ZShyZW1vdGUuYWRkcmVzcyk7XG4gIH0pO1xufVxuSG9tZVJlcG9zaXRvcnlWaWV3TW9kZWwucHJvdG90eXBlLnJlbW92ZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmFwcC5yZXBvTGlzdC5yZW1vdmUodGhpcy5wYXRoKTtcbiAgdGhpcy5ob21lLnVwZGF0ZSgpO1xufVxuXG5mdW5jdGlvbiBIb21lVmlld01vZGVsKGFwcCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHRoaXMuYXBwID0gYXBwO1xuICB0aGlzLnJlcG9zID0ga28ub2JzZXJ2YWJsZUFycmF5KCk7XG4gIHRoaXMuc2hvd051eCA9IGtvLmNvbXB1dGVkKGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiBzZWxmLnJlcG9zKCkubGVuZ3RoID09IDA7XG4gIH0pO1xufVxuSG9tZVZpZXdNb2RlbC5wcm90b3R5cGUudXBkYXRlTm9kZSA9IGZ1bmN0aW9uKHBhcmVudEVsZW1lbnQpIHtcbiAga28ucmVuZGVyVGVtcGxhdGUoJ2hvbWUnLCB0aGlzLCB7fSwgcGFyZW50RWxlbWVudCk7XG59XG5Ib21lVmlld01vZGVsLnByb3RvdHlwZS50ZW1wbGF0ZSA9ICdob21lJztcbkhvbWVWaWV3TW9kZWwucHJvdG90eXBlLnNob3duID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMudXBkYXRlKCk7XG59XG5Ib21lVmlld01vZGVsLnByb3RvdHlwZS51cGRhdGUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuICB2YXIgcmVwb3NCeVBhdGggPSB7fTtcbiAgdGhpcy5yZXBvcygpLmZvckVhY2goZnVuY3Rpb24ocmVwbykgeyByZXBvc0J5UGF0aFtyZXBvLnBhdGhdID0gcmVwbzsgfSk7XG4gIHRoaXMucmVwb3ModGhpcy5hcHAucmVwb0xpc3QoKS5zb3J0KCkubWFwKGZ1bmN0aW9uKHBhdGgpIHtcbiAgICBpZiAoIXJlcG9zQnlQYXRoW3BhdGhdKVxuICAgICAgcmVwb3NCeVBhdGhbcGF0aF0gPSBuZXcgSG9tZVJlcG9zaXRvcnlWaWV3TW9kZWwoc2VsZiwgcGF0aCk7XG4gICAgcmV0dXJuIHJlcG9zQnlQYXRoW3BhdGhdO1xuICB9KSk7XG59XG4iXX0=

View File

@ -1,20 +0,0 @@
.home .nux {
text-align: center;
}
.home .nux .logo-large {
margin-top: 20px;
margin-bottom: 50px;
}
.home .repository {
position: relative;
min-height: 62px;
}
.home .repository.path-removed .list-group-item-heading {
color: #CF5353;
}
.home .repository .glyphicon {
color: #686868;
}
.home .repository:hover .glyphicon {
color: #A5A5A5;
}

View File

@ -1,18 +0,0 @@
<div class="container home animated fadeInLeft" data-ta-container="home-page" data-bind="shown: shown">
<div class="nux" data-bind="visible: showNux">
<img src="images/logoLarge.png" class="logo-large">
<div class="alert alert-info">
<h4>Enter a path to a repository to get started!</h4>
Then press the <span class="glyphicon glyphicon-plus"></span> symbol to make it show up here.
</div>
</div>
<div class="list-group" data-bind="foreach: repos">
<a class="list-group-item repository" data-bind="attr: { href: link }, css: { 'path-removed': pathRemoved }">
<span class="glyphicon glyphicon-arrow-right glyphicon-circled pull-left"></span>
<h4 class="list-group-item-heading" data-bind="text: title"></h4>
<p class="list-group-item-text" data-bind="text: remote"></p>
<button type="button" class="btn btn-default list-item-remove" data-bind="click: remove">&#x2716;</button>
</a>
</div>
</div>

View File

@ -1,62 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
components.register('home', function(args) {
return new HomeViewModel(args.app);
});
function HomeRepositoryViewModel(home, path) {
this.home = home;
this.app = home.app;
this.server = this.app.server;
this.path = path;
this.title = path;
this.link = '/#/repository?path=' + encodeURIComponent(path);
this.pathRemoved = ko.observable(false);
this.remote = ko.observable('...');
this.updateState();
}
HomeRepositoryViewModel.prototype.updateState = function() {
var self = this;
this.server.get('/fs/exists?path=' + encodeURIComponent(this.path), undefined, function(err, exists) {
self.pathRemoved(!exists);
});
this.server.get('/remotes/origin?path=' + encodeURIComponent(this.path), undefined, function(err, remote) {
if (err) {
self.remote('');
return true;
}
self.remote(remote.address);
});
}
HomeRepositoryViewModel.prototype.remove = function() {
this.app.repoList.remove(this.path);
this.home.update();
}
function HomeViewModel(app) {
var self = this;
this.app = app;
this.repos = ko.observableArray();
this.showNux = ko.computed(function() {
return self.repos().length == 0;
});
}
HomeViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('home', this, {}, parentElement);
}
HomeViewModel.prototype.template = 'home';
HomeViewModel.prototype.shown = function() {
this.update();
}
HomeViewModel.prototype.update = function() {
var self = this;
var reposByPath = {};
this.repos().forEach(function(repo) { reposByPath[repo.path] = repo; });
this.repos(this.app.repoList().sort().map(function(path) {
if (!reposByPath[path])
reposByPath[path] = new HomeRepositoryViewModel(self, path);
return reposByPath[path];
}));
}

View File

@ -1,25 +0,0 @@
.home {
.nux {
text-align: center;
.logo-large {
margin-top: 20px;
margin-bottom: 50px;
}
}
.repository {
position: relative;
min-height: 62px;
&.path-removed {
.list-group-item-heading {
color: #CF5353;
}
}
.glyphicon {
color: #686868;
}
&:hover .glyphicon {
color: #A5A5A5;
}
}
}

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"home": "home.html"
},
"javascript": "home.bundle.js",
"css": "home.css"
}
}

View File

@ -1,44 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
components.register('imagediff', function(args) {
return new ImageDiffViewModel(args);
});
var ImageDiffViewModel = function(args) {
var self = this;
this.filename = args.filename;
this.repoPath = args.repoPath;
this.isNew = ko.observable(false);
this.isRemoved = ko.observable(false);
this.sha1 = args.sha1;
this.state = ko.computed(function() {
if (self.isNew()) return 'new';
if (self.isRemoved()) return 'removed';
return 'changed';
});
this.oldImageSrc = ko.computed(function() {
return '/api/diff/image?path=' + encodeURIComponent(self.repoPath()) + '&filename=' + self.filename + '&version=' + (self.sha1 ? self.sha1 + '^': 'HEAD');
});
this.newImageSrc = ko.computed(function() {
return '/api/diff/image?path=' + encodeURIComponent(self.repoPath()) + '&filename=' + self.filename + '&version=' + (self.sha1 ? self.sha1: 'current');
});
this.isShowingDiffs = args.isShowingDiffs;
}
ImageDiffViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('imagediff', this, {}, parentElement);
}
ImageDiffViewModel.prototype.invalidateDiff = function(callback) {
if (callback) callback();
}
ImageDiffViewModel.prototype.newImageError = function(data, event) {
this.isRemoved(true);
}
ImageDiffViewModel.prototype.oldImageError = function(data, event) {
this.isNew(true);
}
},{"knockout":"knockout","ungit-components":"ungit-components"}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL2ltYWdlZGlmZi9pbWFnZWRpZmYuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIlxudmFyIGtvID0gcmVxdWlyZSgna25vY2tvdXQnKTtcbnZhciBjb21wb25lbnRzID0gcmVxdWlyZSgndW5naXQtY29tcG9uZW50cycpO1xuXG5jb21wb25lbnRzLnJlZ2lzdGVyKCdpbWFnZWRpZmYnLCBmdW5jdGlvbihhcmdzKSB7XG4gIHJldHVybiBuZXcgSW1hZ2VEaWZmVmlld01vZGVsKGFyZ3MpO1xufSk7XG5cbnZhciBJbWFnZURpZmZWaWV3TW9kZWwgPSBmdW5jdGlvbihhcmdzKSB7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgdGhpcy5maWxlbmFtZSA9IGFyZ3MuZmlsZW5hbWU7XG4gIHRoaXMucmVwb1BhdGggPSBhcmdzLnJlcG9QYXRoO1xuICB0aGlzLmlzTmV3ID0ga28ub2JzZXJ2YWJsZShmYWxzZSk7XG4gIHRoaXMuaXNSZW1vdmVkID0ga28ub2JzZXJ2YWJsZShmYWxzZSk7XG4gIHRoaXMuc2hhMSA9IGFyZ3Muc2hhMTtcbiAgdGhpcy5zdGF0ZSA9IGtvLmNvbXB1dGVkKGZ1bmN0aW9uKCkge1xuICAgIGlmIChzZWxmLmlzTmV3KCkpIHJldHVybiAnbmV3JztcbiAgICBpZiAoc2VsZi5pc1JlbW92ZWQoKSkgcmV0dXJuICdyZW1vdmVkJztcbiAgICByZXR1cm4gJ2NoYW5nZWQnO1xuICB9KTtcbiAgdGhpcy5vbGRJbWFnZVNyYyA9IGtvLmNvbXB1dGVkKGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiAnL2FwaS9kaWZmL2ltYWdlP3BhdGg9JyArIGVuY29kZVVSSUNvbXBvbmVudChzZWxmLnJlcG9QYXRoKCkpICsgJyZmaWxlbmFtZT0nICsgc2VsZi5maWxlbmFtZSArICcmdmVyc2lvbj0nICsgKHNlbGYuc2hhMSA/IHNlbGYuc2hhMSArICdeJzogJ0hFQUQnKTtcbiAgfSk7XG4gIHRoaXMubmV3SW1hZ2VTcmMgPSBrby5jb21wdXRlZChmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gJy9hcGkvZGlmZi9pbWFnZT9wYXRoPScgKyBlbmNvZGVVUklDb21wb25lbnQoc2VsZi5yZXBvUGF0aCgpKSArICcmZmlsZW5hbWU9JyArIHNlbGYuZmlsZW5hbWUgKyAnJnZlcnNpb249JyArIChzZWxmLnNoYTEgPyBzZWxmLnNoYTE6ICdjdXJyZW50Jyk7XG4gIH0pO1xuICB0aGlzLmlzU2hvd2luZ0RpZmZzID0gYXJncy5pc1Nob3dpbmdEaWZmcztcbn1cbkltYWdlRGlmZlZpZXdNb2RlbC5wcm90b3R5cGUudXBkYXRlTm9kZSA9IGZ1bmN0aW9uKHBhcmVudEVsZW1lbnQpIHtcbiAga28ucmVuZGVyVGVtcGxhdGUoJ2ltYWdlZGlmZicsIHRoaXMsIHt9LCBwYXJlbnRFbGVtZW50KTtcbn1cbkltYWdlRGlmZlZpZXdNb2RlbC5wcm90b3R5cGUuaW52YWxpZGF0ZURpZmYgPSBmdW5jdGlvbihjYWxsYmFjaykge1xuICBpZiAoY2FsbGJhY2spIGNhbGxiYWNrKCk7XG59XG5JbWFnZURpZmZWaWV3TW9kZWwucHJvdG90eXBlLm5ld0ltYWdlRXJyb3IgPSBmdW5jdGlvbihkYXRhLCBldmVudCkge1xuICB0aGlzLmlzUmVtb3ZlZCh0cnVlKTtcbn1cbkltYWdlRGlmZlZpZXdNb2RlbC5wcm90b3R5cGUub2xkSW1hZ2VFcnJvciA9IGZ1bmN0aW9uKGRhdGEsIGV2ZW50KSB7XG4gIHRoaXMuaXNOZXcodHJ1ZSk7XG59XG4iXX0=

View File

@ -1,16 +0,0 @@
.imageDiff {
padding: 10px;
text-align: center;
}
.imageDiff .glyphicon-arrow-right {
font-size: 104px;
color: rgba(0, 0, 0, 0.3);
}
.img-removed {
background-color: rgba(230, 70, 100, 0.2);
margin-top: 20px;
}
.img-added {
background-color: rgba(70, 230, 100, 0.2);
margin-top: 20px;
}

View File

@ -1,27 +0,0 @@
<!-- ko if: isShowingDiffs -->
<!-- ko if: state() == 'new' -->
<div class="imageDiff img-added">
<img data-bind="attr: { src: newImageSrc }" class="img-responsive">
</div>
<!-- /ko -->
<!-- ko if: state() == 'removed' -->
<div class="imageDiff img-removed">
<img data-bind="attr: { src: oldImageSrc }" class="img-responsive img-removed">
</div>
<!-- /ko -->
<!-- ko if: state() == 'changed' -->
<div class="imageDiff">
<div class="row">
<div class="col-lg-5">
<img data-bind="event: {error: oldImageError }, attr: { src: oldImageSrc }" class="img-responsive">
</div>
<div class="col-lg-2">
<span class="glyphicon glyphicon-arrow-right"></span>
</div>
<div class="col-lg-5">
<img data-bind="event: {error: newImageError }, attr: { src: newImageSrc }" class="img-responsive">
</div>
</div>
</div>
<!-- /ko -->
<!-- /ko -->

View File

@ -1,40 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
components.register('imagediff', function(args) {
return new ImageDiffViewModel(args);
});
var ImageDiffViewModel = function(args) {
var self = this;
this.filename = args.filename;
this.repoPath = args.repoPath;
this.isNew = ko.observable(false);
this.isRemoved = ko.observable(false);
this.sha1 = args.sha1;
this.state = ko.computed(function() {
if (self.isNew()) return 'new';
if (self.isRemoved()) return 'removed';
return 'changed';
});
this.oldImageSrc = ko.computed(function() {
return '/api/diff/image?path=' + encodeURIComponent(self.repoPath()) + '&filename=' + self.filename + '&version=' + (self.sha1 ? self.sha1 + '^': 'HEAD');
});
this.newImageSrc = ko.computed(function() {
return '/api/diff/image?path=' + encodeURIComponent(self.repoPath()) + '&filename=' + self.filename + '&version=' + (self.sha1 ? self.sha1: 'current');
});
this.isShowingDiffs = args.isShowingDiffs;
}
ImageDiffViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('imagediff', this, {}, parentElement);
}
ImageDiffViewModel.prototype.invalidateDiff = function(callback) {
if (callback) callback();
}
ImageDiffViewModel.prototype.newImageError = function(data, event) {
this.isRemoved(true);
}
ImageDiffViewModel.prototype.oldImageError = function(data, event) {
this.isNew(true);
}

View File

@ -1,19 +0,0 @@
.imageDiff {
padding: 10px;
text-align: center;
.glyphicon-arrow-right {
font-size: 104px;
color: rgba(0, 0, 0, 0.3);
}
}
.img-removed {
background-color: rgba(230, 70, 100, 0.2);
margin-top: 20px;
}
.img-added {
background-color: rgba(70, 230, 100, 0.2);
margin-top: 20px;
}

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"imagediff": "imagediff.html"
},
"javascript": "imagediff.bundle.js",
"css": "imagediff.css"
}
}

View File

@ -1,47 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
var signals = require('signals');
components.register('login', function(args) {
return new LoginViewModel(args.server);
});
var LoginViewModel = function(server) {
var self = this;
this.server = server;
this.loggedIn = new signals.Signal();
this.status = ko.observable('loading');
this.username = ko.observable();
this.password = ko.observable();
this.loginError = ko.observable();
this.server.get('/loggedin', undefined, function(err, status) {
if (status.loggedIn) {
self.loggedIn.dispatch();
self.status('loggedIn');
}
else self.status('login');
});
}
LoginViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('login', this, {}, parentElement);
}
LoginViewModel.prototype.login = function() {
var self = this;
this.server.post('/login', { username: this.username(), password: this.password() }, function(err, res) {
if (err) {
if (err.res.body.error) {
self.loginError(err.res.body.error);
return true;
}
} else {
self.loggedIn.dispatch();
self.status('loggedIn');
}
});
}
},{"knockout":"knockout","signals":undefined,"ungit-components":"ungit-components"}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL2xvZ2luL2xvZ2luLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJcbnZhciBrbyA9IHJlcXVpcmUoJ2tub2Nrb3V0Jyk7XG52YXIgY29tcG9uZW50cyA9IHJlcXVpcmUoJ3VuZ2l0LWNvbXBvbmVudHMnKTtcbnZhciBzaWduYWxzID0gcmVxdWlyZSgnc2lnbmFscycpO1xuXG5jb21wb25lbnRzLnJlZ2lzdGVyKCdsb2dpbicsIGZ1bmN0aW9uKGFyZ3MpIHtcbiAgcmV0dXJuIG5ldyBMb2dpblZpZXdNb2RlbChhcmdzLnNlcnZlcik7XG59KTtcblxudmFyIExvZ2luVmlld01vZGVsID0gZnVuY3Rpb24oc2VydmVyKSB7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXI7XG4gIHRoaXMubG9nZ2VkSW4gPSBuZXcgc2lnbmFscy5TaWduYWwoKTtcbiAgdGhpcy5zdGF0dXMgPSBrby5vYnNlcnZhYmxlKCdsb2FkaW5nJyk7XG4gIHRoaXMudXNlcm5hbWUgPSBrby5vYnNlcnZhYmxlKCk7XG4gIHRoaXMucGFzc3dvcmQgPSBrby5vYnNlcnZhYmxlKCk7XG4gIHRoaXMubG9naW5FcnJvciA9IGtvLm9ic2VydmFibGUoKTtcbiAgdGhpcy5zZXJ2ZXIuZ2V0KCcvbG9nZ2VkaW4nLCB1bmRlZmluZWQsIGZ1bmN0aW9uKGVyciwgc3RhdHVzKSB7XG4gICAgaWYgKHN0YXR1cy5sb2dnZWRJbikge1xuICAgICAgc2VsZi5sb2dnZWRJbi5kaXNwYXRjaCgpO1xuICAgICAgc2VsZi5zdGF0dXMoJ2xvZ2dlZEluJyk7XG4gICAgfVxuICAgIGVsc2Ugc2VsZi5zdGF0dXMoJ2xvZ2luJyk7XG4gIH0pO1xufVxuTG9naW5WaWV3TW9kZWwucHJvdG90eXBlLnVwZGF0ZU5vZGUgPSBmdW5jdGlvbihwYXJlbnRFbGVtZW50KSB7XG4gIGtvLnJlbmRlclRlbXBsYXRlKCdsb2dpbicsIHRoaXMsIHt9LCBwYXJlbnRFbGVtZW50KTtcbn1cbkxvZ2luVmlld01vZGVsLnByb3RvdHlwZS5sb2dpbiA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHRoaXMuc2VydmVyLnBvc3QoJy9sb2dpbicsIHsgdXNlcm5hbWU6IHRoaXMudXNlcm5hbWUoKSwgcGFzc3dvcmQ6IHRoaXMucGFzc3dvcmQoKSB9LCBmdW5jdGlvbihlcnIsIHJlcykge1xuICAgIGlmIChlcnIpIHtcbiAgICAgIGlmIChlcnIucmVzLmJvZHkuZXJyb3IpIHtcbiAgICAgICAgc2VsZi5sb2dpbkVycm9yKGVyci5yZXMuYm9keS5lcnJvcik7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBzZWxmLmxvZ2dlZEluLmRpc3BhdGNoKCk7XG4gICAgICBzZWxmLnN0YXR1cygnbG9nZ2VkSW4nKTtcbiAgICB9XG4gIH0pO1xufVxuXG4iXX0=

View File

@ -1,26 +0,0 @@
<div class="container">
<div data-bind="visible: status() == 'loading'">
</div>
<div class='login col-lg-6' data-bind="visible: status() == 'login'" data-ta-container="login-page">
<h1>Login</h1>
<!-- ko if: loginError -->
<div class="loginError" data-ta-element="login-error" data-bind="text: loginError"></div>
<!-- /ko -->
<form data-bind="submit: login" role="form">
<div class="form-group">
<label for="inputUsername">Username</label>
<input type="text" class="form-control" id="inputUsername" placeholder="Username" data-bind="value: username" data-ta-input="username">
</div>
<div class="form-group">
<label for="inputPassword">Password</label>
<input type="password" class="form-control" id="inputPassword" placeholder="Password" data-bind="value: password" data-ta-input="password">
</div>
<input type="submit" class="btn btn-primary" value="Login" data-ta-clickable="submit">
</form>
</div>
</div>

View File

@ -1,43 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var signals = require('signals');
components.register('login', function(args) {
return new LoginViewModel(args.server);
});
var LoginViewModel = function(server) {
var self = this;
this.server = server;
this.loggedIn = new signals.Signal();
this.status = ko.observable('loading');
this.username = ko.observable();
this.password = ko.observable();
this.loginError = ko.observable();
this.server.get('/loggedin', undefined, function(err, status) {
if (status.loggedIn) {
self.loggedIn.dispatch();
self.status('loggedIn');
}
else self.status('login');
});
}
LoginViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('login', this, {}, parentElement);
}
LoginViewModel.prototype.login = function() {
var self = this;
this.server.post('/login', { username: this.username(), password: this.password() }, function(err, res) {
if (err) {
if (err.res.body.error) {
self.loginError(err.res.body.error);
return true;
}
} else {
self.loggedIn.dispatch();
self.status('loggedIn');
}
});
}

View File

@ -1,8 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"login": "login.html"
},
"javascript": "login.bundle.js"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,74 +0,0 @@
<div class="path" data-bind="shown: shown" data-ta-container="path-page">
<!-- ko if: status() == 'loading' -->
<div class='animated fadeInLeft'>
<!-- ko component: loadingProgressBar --><!-- /ko -->
</div>
<!-- /ko -->
<!-- ko if: status() == 'cloning' -->
<div class='animated fadeInLeft'>
<h2>Cloning...</h2>
<!-- ko component: cloningProgressBar --><!-- /ko -->
</div>
<!-- /ko -->
<!-- ko if: status() == 'uninited' -->
<div class="uninited" data-ta-container="uninited-path-page">
<div class="container">
<div class="alert alert-info" data-bind="visible: showDirectoryCreatedAlert">
Directory '<span data-bind="text: dirName"></span>' created
</div>
<h1>'<span data-bind="text: dirName"></span>' is not a repository</h1>
<p>There is no repository at the selected path.</p>
<div class="row">
<div class="col-lg-6">
<div class="panel panel-default">
<div class="panel-heading">Create a new repository</div>
<div class="panel-body">
<button data-ta-clickable="init-repository" class="btn btn-primary btn-lg" data-bind='click: initRepository'>
Make '<span data-bind="text: dirName"></span>' a repository
</button>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="panel panel-default">
<div class="panel-heading">Clone a repository into a subfolder of '<span data-bind="text: dirName"></span>'</div>
<div class="panel-body">
<form data-bind="submit: cloneRepository">
<div class="form-group">
<label for="cloneFromInput">Clone from</label>
<input class="form-control" type="text" data-ta-input="clone-url" id="cloneFromInput" placeholder="url" data-bind="value: cloneUrl, valueUpdate: 'afterkeydown'">
</div>
<div class="form-group">
<label for="cloneToInput">to</label>
<input class="form-control" type="text" data-ta-input="clone-target" id="cloneToInput" data-bind="value: cloneDestination, attr: { placeholder: cloneDestinationImplicit }">
</div>
<input type="submit" class="btn btn-primary btn-lg" data-ta-clickable="clone-repository" value="Clone repository">
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /ko -->
<!-- ko if: status() == 'no-such-path' -->
<div data-ta-container="invalid-path" data-bind="visible: status() == 'no-such-path'">
<h2>Invalid path</h2>
<p>"<span data-bind="text: path"></span>" doesn&#39;t seem to be a valid path.</p>
<div class="create-dir">
<button class="btn btn-primary btn-lg" data-ta-clickable="create-dir" data-bind="click: createDir">Create directory</button>
</div>
</div>
<!-- /ko -->
<!-- ko if: repository -->
<!-- ko component: repository --><!-- /ko -->
<!-- /ko -->
</div>

View File

@ -1,105 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var addressParser = require('ungit-address-parser');
var navigation = require('ungit-navigation');
var programEvents = require('ungit-program-events');
components.register('path', function(args) {
return new PathViewModel(args.server, args.path);
});
var PathViewModel = function(server, path) {
var self = this;
this.server = server;
this.repoPath = ko.observable(path);
this.dirName = this.repoPath().replace('\\', '/')
.split('/')
.filter(function(s) { return s; })
.slice(-1)[0] || '/';
this.status = ko.observable('loading');
this.loadingProgressBar = components.create('progressBar', { predictionMemoryKey: 'path-loading-' + path });
this.loadingProgressBar.start();
this.cloningProgressBar = components.create('progressBar', {
predictionMemoryKey: 'path-cloning-' + path,
fallbackPredictedTimeMs: 10000
});
this.cloneUrl = ko.observable();
this.showDirectoryCreatedAlert = ko.observable(false);
this.cloneDestinationImplicit = ko.computed(function() {
var defaultText = 'destination folder';
if (!self.cloneUrl()) return defaultText;
var parsedAddress = addressParser.parseAddress(self.cloneUrl());
return parsedAddress.shortProject || defaultText;
});
this.cloneDestination = ko.observable();
this.repository = ko.observable();
}
PathViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('path', this, {}, parentElement);
}
PathViewModel.prototype.template = 'path';
PathViewModel.prototype.shown = function() {
this.updateStatus();
}
PathViewModel.prototype.updateAnimationFrame = function(deltaT) {
if (this.repository())
this.repository().updateAnimationFrame(deltaT);
}
PathViewModel.prototype.updateStatus = function() {
var self = this;
this.server.get('/quickstatus', { path: this.repoPath() }, function(err, status){
self.loadingProgressBar.stop();
if (err) return;
if (status.type == 'inited' || status.type == 'bare') {
if (self.repoPath() !== status.gitRootPath) {
self.repoPath(status.gitRootPath);
programEvents.dispatch({ event: 'navigated-to-path', path: self.repoPath() });
programEvents.dispatch({ event: 'working-tree-changed' });
}
self.status(status.type);
if (!self.repository()) {
self.repository(components.create('repository', { server: self.server, path: self }));
}
} else if (status.type == 'uninited' || status.type == 'no-such-path') {
self.status(status.type);
self.repository(null);
}
});
}
PathViewModel.prototype.initRepository = function() {
var self = this;
this.server.post('/init', { path: this.repoPath() }, function(err, res) {
if (err) return;
self.updateStatus();
});
}
PathViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'request-credentials') this.cloningProgressBar.pause();
else if (event.event == 'request-credentials-response') this.cloningProgressBar.unpause();
else if (event.event == 'working-tree-changed') this.updateStatus();
else if (event.event == 'request-app-content-refresh') this.updateStatus();
if (this.repository()) this.repository().onProgramEvent(event);
}
PathViewModel.prototype.cloneRepository = function() {
var self = this;
self.status('cloning');
this.cloningProgressBar.start();
var dest = this.cloneDestination() || this.cloneDestinationImplicit();
this.server.post('/clone', { path: this.repoPath(), url: this.cloneUrl(), destinationDir: dest }, function(err, res) {
self.cloningProgressBar.stop();
if (err) return;
navigation.browseTo('repository?path=' + encodeURIComponent(res.path));
});
}
PathViewModel.prototype.createDir = function() {
var self = this;
this.showDirectoryCreatedAlert(true);
this.server.post('/createDir', { dir: this.repoPath() }, function() {
self.updateStatus();
});
}

View File

@ -1,8 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"path": "path.html"
},
"javascript": "path.bundle.js"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
<!-- Always shown -->
<script type="text/html" id="progressBar">
<div class="progress" data-ta-element="progress-bar">
<div class="progress-bar" data-bind="attr: { style: style }"></div>
</div>
</script>
<!-- Only visible while it's running -->
<script type="text/html" id="temporaryProgressBar">
<!-- ko if: running -->
<!-- ko template: { name: 'progressBar', data: $data } --><!-- /ko -->
<!-- /ko -->
</script>

View File

@ -1,89 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
components.register('progressBar', function(args) {
return new ProgressBarViewModel(args.predictionMemoryKey, args.fallbackPredictedTimeMs, args.temporary);
});
var ProgressBarViewModel = function(predictionMemoryKey, fallbackPredictedTimeMs, temporary) {
var self = this;
if (fallbackPredictedTimeMs === undefined) fallbackPredictedTimeMs = 1000;
this.temporary = temporary;
this.style = ko.observable();
this.running = ko.observable(false);
self._width = ko.observable(0);
self._opacity = ko.observable(1);
self._widthSpeed = ko.observable(0);
self._opacitySpeed = ko.observable(0);
self._animationState = ko.observable('running');
this.style = ko.computed(function() {
return 'width: ' + self._width() + '%; ' +
'opacity: ' + self._opacity() + '; ' +
'-webkit-transition: width ' + self._widthSpeed() + 'ms, opacity ' + self._opacitySpeed() + 'ms;' +
'transition: width ' + self._widthSpeed() + 'ms, opacity ' + self._opacitySpeed() + 'ms; ' +
'animation-play-state: ' + self._animationState();
});
this.predictionMemoryKey = 'predict-' + predictionMemoryKey;
this.isFirstRun = ko.observable(false);
this.fallbackPredictedTimeMs = fallbackPredictedTimeMs;
}
ProgressBarViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate(this.temporary ? 'temporaryProgressBar' : 'progressBar', this, {}, parentElement);
}
ProgressBarViewModel.prototype.start = function() {
if (this.running()) return;
var self = this;
var predictionMs = localStorage.getItem(this.predictionMemoryKey);
if (!predictionMs || isNaN(predictionMs)) {
this.isFirstRun(true);
predictionMs = this.fallbackPredictedTimeMs;
} else {
predictionMs = parseInt(predictionMs);
}
this.predictionMs = predictionMs;
this._width(0);
this._opacity(1);
this._opacitySpeed(0);
this._widthSpeed(0);
this._animationState('running');
this.running(true);
this.startMs = Date.now();
this.pausedMs = 0;
setTimeout(function(){
predictionMs = Math.max(500, predictionMs);
self._width(80);
self._widthSpeed(predictionMs);
}, 1);
}
ProgressBarViewModel.prototype.pause = function() {
this._animationState('paused');
this.pauseStartMs = Date.now();
}
ProgressBarViewModel.prototype.unpause = function() {
this._animationState('running');
this.pausedMs += Date.now() - this.pauseStartMs;
}
ProgressBarViewModel.prototype.stop = function() {
var self = this;
var elapsedMs = Date.now() - this.startMs - this.pausedMs;
var newPrediction;
if (self.isFirstRun()) {
self.isFirstRun(false);
newPrediction = elapsedMs;
} else {
newPrediction = elapsedMs * 0.1 + self.predictionMs * 0.9;
}
localStorage.setItem(self.predictionMemoryKey, newPrediction.toString());
self._width(100);
self._widthSpeed(300);
setTimeout(function() {
self._opacity(0);
self._opacitySpeed(300);
setTimeout(function() {
self.running(false);
}, 310);
}, 400);
}

View File

@ -1,6 +0,0 @@
{
"exports": {
"raw": "progressBar.html",
"javascript": "progressBar.bundle.js"
}
}

View File

@ -1,12 +0,0 @@
.toolbar .refresh-button {
background: rgba(0, 0, 0, 0.1);
border: 0px;
font-size: 22px;
}
.repository-actions .refresh-button {
background: #546565;
border: 0px;
font-size: 20px;
height: 34px;
padding-top: 3px;
}

View File

@ -1,28 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var ko = require('knockout');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
components.register('refreshbutton', function() {
return new RefreshButton();
});
function RefreshButton() {
this.refreshingProgressBar = components.create('progressBar', { predictionMemoryKey: 'refreshing-content', temporary: true });
}
RefreshButton.prototype.refresh = function() {
var self = this;
programEvents.dispatch({ event: 'request-app-content-refresh' });
this.refreshingProgressBar.start();
setTimeout(function() { // Fake the progress bar, for now (since we don't really know who and when this message will be handled)
self.refreshingProgressBar.stop();
}, 100);
return true;
}
RefreshButton.prototype.updateNode = function(parentElement) {
ko.renderTemplate('refreshbutton', this, {}, parentElement);
}
},{"knockout":"knockout","ungit-components":"ungit-components","ungit-program-events":"ungit-program-events"}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJjb21wb25lbnRzL3JlZnJlc2hCdXR0b24vcmVmcmVzaEJ1dHRvbi5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIlxudmFyIGtvID0gcmVxdWlyZSgna25vY2tvdXQnKTtcbnZhciBjb21wb25lbnRzID0gcmVxdWlyZSgndW5naXQtY29tcG9uZW50cycpO1xudmFyIHByb2dyYW1FdmVudHMgPSByZXF1aXJlKCd1bmdpdC1wcm9ncmFtLWV2ZW50cycpO1xuXG5jb21wb25lbnRzLnJlZ2lzdGVyKCdyZWZyZXNoYnV0dG9uJywgZnVuY3Rpb24oKSB7XG4gIHJldHVybiBuZXcgUmVmcmVzaEJ1dHRvbigpO1xufSk7XG5cbmZ1bmN0aW9uIFJlZnJlc2hCdXR0b24oKSB7XG4gIHRoaXMucmVmcmVzaGluZ1Byb2dyZXNzQmFyID0gY29tcG9uZW50cy5jcmVhdGUoJ3Byb2dyZXNzQmFyJywgeyBwcmVkaWN0aW9uTWVtb3J5S2V5OiAncmVmcmVzaGluZy1jb250ZW50JywgdGVtcG9yYXJ5OiB0cnVlIH0pO1xufVxuUmVmcmVzaEJ1dHRvbi5wcm90b3R5cGUucmVmcmVzaCA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHByb2dyYW1FdmVudHMuZGlzcGF0Y2goeyBldmVudDogJ3JlcXVlc3QtYXBwLWNvbnRlbnQtcmVmcmVzaCcgfSk7XG4gIHRoaXMucmVmcmVzaGluZ1Byb2dyZXNzQmFyLnN0YXJ0KCk7XG4gIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7IC8vIEZha2UgdGhlIHByb2dyZXNzIGJhciwgZm9yIG5vdyAoc2luY2Ugd2UgZG9uJ3QgcmVhbGx5IGtub3cgd2hvIGFuZCB3aGVuIHRoaXMgbWVzc2FnZSB3aWxsIGJlIGhhbmRsZWQpXG4gICAgc2VsZi5yZWZyZXNoaW5nUHJvZ3Jlc3NCYXIuc3RvcCgpO1xuICB9LCAxMDApO1xuICByZXR1cm4gdHJ1ZTtcbn1cblJlZnJlc2hCdXR0b24ucHJvdG90eXBlLnVwZGF0ZU5vZGUgPSBmdW5jdGlvbihwYXJlbnRFbGVtZW50KSB7XG4gIGtvLnJlbmRlclRlbXBsYXRlKCdyZWZyZXNoYnV0dG9uJywgdGhpcywge30sIHBhcmVudEVsZW1lbnQpO1xufVxuIl19

View File

@ -1,5 +0,0 @@
<button class="btn btn-default refresh-button bootstrap-tooltip" data-bind="click: refresh" data-toggle="tooltip" data-placement="bottom" data-original-title="Refresh changes" data-delay='{"show":"2000", "hide":"0"}' data-ta-clickable="refresh-button">
<span class="glyphicon glyphicon-refresh"></span>
<!-- ko component: refreshingProgressBar --><!-- /ko -->
</button>

View File

@ -1,24 +0,0 @@
var ko = require('knockout');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
components.register('refreshbutton', function() {
return new RefreshButton();
});
function RefreshButton() {
this.refreshingProgressBar = components.create('progressBar', { predictionMemoryKey: 'refreshing-content', temporary: true });
}
RefreshButton.prototype.refresh = function() {
var self = this;
programEvents.dispatch({ event: 'request-app-content-refresh' });
this.refreshingProgressBar.start();
setTimeout(function() { // Fake the progress bar, for now (since we don't really know who and when this message will be handled)
self.refreshingProgressBar.stop();
}, 100);
return true;
}
RefreshButton.prototype.updateNode = function(parentElement) {
ko.renderTemplate('refreshbutton', this, {}, parentElement);
}

View File

@ -1,18 +0,0 @@
.toolbar {
.refresh-button {
background: rgba(0, 0, 0, 0.1);
border: 0px;
font-size: 22px;
}
}
.repository-actions {
.refresh-button {
background: rgba(84, 101, 101, 1);
border: 0px;
font-size: 20px;
height: 34px;
padding-top: 3px;
}
}

View File

@ -1,9 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"refreshbutton": "refreshbutton.html"
},
"javascript": "refreshbutton.bundle.js",
"css": "refreshbutton.css"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,20 +0,0 @@
<div class="btn-group fetchButton">
<button type="button" class="btn btn-default btn-main" data-ta-clickable="fetch" data-bind="click: clickFetch, enable: fetchEnabled">
<span class="glyphicon glyphicon-download-alt"></span>
<span data-bind="text: fetchLabel"></span>
<!-- ko component: fetchingProgressBar --><!-- /ko -->
</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-ta-clickable="remotes-menu">
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu" data-ta-container="remotes">
<!-- ko foreach: remotes -->
<li>
<a href="#" data-bind="text: name, click: changeRemote, attr: { 'data-ta-clickable': name }"></a>
<a href="#" class="list-link list-remove" data-bind="text: 'X', click: $parent.remoteRemove.bind($parent), attr: { 'data-ta-clickable': name + '-remove' }"></a>
</li>
<!-- /ko -->
<li class="divider"></li>
<li><a href="#" data-bind="click: showAddRemoteDialog" data-ta-clickable="show-add-remote-dialog">Add a new remote</a></li>
</ul>
</div>

View File

@ -1,115 +0,0 @@
var ko = require('knockout');
var _ = require('lodash');
var async = require('async');
var components = require('ungit-components');
var programEvents = require('ungit-program-events');
components.register('remotes', function(args) {
return new RemotesViewModel(args.server, args.repoPath);
});
function RemotesViewModel(server, repoPath) {
var self = this;
this.repoPath = repoPath;
this.server = server;
this.remotes = ko.observable([]);
this.currentRemote = ko.observable(null);
this.currentRemote.subscribe(function(value) {
programEvents.dispatch({ event: 'current-remote-changed', newRemote: value });
});
this.fetchLabel = ko.computed(function() {
if (self.currentRemote()) return 'Fetch from ' + self.currentRemote();
else return 'No remotes specified';
})
this.fetchingProgressBar = components.create('progressBar', { predictionMemoryKey: 'fetching-' + this.repoPath(), temporary: true });
this.fetchEnabled = ko.computed(function() {
return self.remotes().length > 0;
});
this.shouldAutoFetch = ungit.config.autoFetch;
this.updateRemotes();
}
RemotesViewModel.prototype.updateNode = function(parentElement) {
ko.renderTemplate('remotes', this, {}, parentElement);
}
RemotesViewModel.prototype.clickFetch = function() { this.fetch({ nodes: true, tags: true }); }
RemotesViewModel.prototype.onProgramEvent = function(event) {
if (event.event == 'request-credentials') this.fetchingProgressBar.pause();
else if (event.event == 'request-credentials-response') this.fetchingProgressBar.unpause();
else if (event.event == 'request-fetch-tags') this.fetch({ tags: true });
}
RemotesViewModel.prototype.fetch = function(options) {
if (this.fetchingProgressBar.running()) return;
var self = this;
this.fetchingProgressBar.start();
var jobs = [];
if (options.tags) jobs.push(function(done) { self.server.get('/remote/tags', { path: self.repoPath(), remote: self.currentRemote() }, done); });
if (options.nodes) jobs.push(function(done) { self.server.post('/fetch', { path: self.repoPath(), remote: self.currentRemote() }, done); });
async.parallel(jobs, function(err, result) {
self.fetchingProgressBar.stop();
if (!err && options.tags) programEvents.dispatch({ event: 'remote-tags-update', tags: result[0] });
});
}
RemotesViewModel.prototype.updateRemotes = function() {
var self = this;
this.server.get('/remotes', { path: this.repoPath() }, function(err, remotes) {
if (err && err.errorCode == 'not-a-repository') return true;
if (err) return;
remotes = remotes.map(function(remote) {
return {
name: remote,
changeRemote: function() { self.currentRemote(remote) }
}
});
self.remotes(remotes);
if (!self.currentRemote() && remotes.length > 0) {
if (_.find(remotes, { 'name': 'origin' })) // default to origin if it exists
self.currentRemote('origin');
else // otherwise take the first one
self.currentRemote(remotes[0].name);
if (self.shouldAutoFetch) {
self.fetch({ nodes: true, tags: true });
}
}
self.shouldAutoFetch = false;
});
}
RemotesViewModel.prototype.showAddRemoteDialog = function() {
var self = this;
var diag = components.create('addremotedialog');
diag.closed.add(function() {
if (diag.isSubmitted()) {
self.server.post('/remotes/' + encodeURIComponent(diag.name()), { path: self.repoPath(), url: diag.url() }, function(err, res) {
if (err) return;
self.updateRemotes();
})
}
});
programEvents.dispatch({ event: 'request-show-dialog', dialog: diag });
}
RemotesViewModel.prototype.remoteRemove = function(remote) {
var self = this;
var diag = components.create('yesnodialog', { title: 'Are you sure?', details: 'Deleting ' + remote.name + ' remote cannot be undone with ungit.'});
diag.closed.add(function() {
if (diag.result()) {
self.fetchingProgressBar.start();
self.server.del('/remotes/' + remote.name, { path: self.repoPath() }, function(err, result) {
if (err) {
console.log(err);
return;
}
self.updateRemotes();
self.fetchingProgressBar.stop();
});
}
});
programEvents.dispatch({ event: 'request-show-dialog', dialog: diag });
}

View File

@ -1,8 +0,0 @@
{
"exports": {
"knockoutTemplates": {
"remotes": "remotes.html"
},
"javascript": "remotes.bundle.js"
}
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More