<commit_before>/*
 * helpers.js: Test helpers for haibu
 *
 * (C) 2011 Nodejitsu Inc.
 * MIT LICENSE
 *
 */

var assert = require('assert'),
    exec = require('child_process').exec,
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    request = require('request'),
    haibu = require('../lib/haibu');

var helpers = exports,
    testConfig;

function showConfigWarning (requireAuth) {
  if ((!testConfig || !testConfig.auth ||
      testConfig.auth.username === 'test-username' ||
      testConfig.auth.apiKey === 'test-apiKey')
      && requireAuth) {
    console.warn("Config file test/fixtures/test-config.json doesn't have valid data. Skipping remote tests");
  }
}

helpers.loadConfig = function (requireAuth) {
  function showConfig () {
    showConfigWarning(requireAuth);
    return testConfig;
  }

  if (testConfig) {
    return showConfig();
  }

  try {
    var configFile = path.join(__dirname, 'fixtures', 'test-config.json'),
        config = JSON.parse(fs.readFileSync(configFile).toString());

    testConfig = config;
    return showConfig();
  }
  catch (ex) {
    return showConfig();
  }
};

helpers.cleanAutostart = function (callback) {
  exec('rm -rf autostart/*', callback);
};

helpers.init = function (callback) {
  var config = helpers.loadConfig() || {};
  helpers.cleanAutostart(function () {
    if (config) {
      Object.keys(config).forEach(function (key) {
        haibu.config.set(key, config[key]);
      });
    }

    haibu.init(callback);
  });
};

helpers.start = function (port, callback) {
  helpers.init(function (err) {
    haibu.drone.start({
      minUptime: 0,
      port: port,
      maxRestart: 2,
      init: false
    }, function (err, server) {
      return callback(err, server);
    });
  });
};

helpers.startHook = function (callback) {
  helpers.init(function (err) {
    haibu.drone.startHook(callback);
  });
}

helpers.requireInit = function (initialized) {
  return {
    "This test requires haibu.init": {
      topic: function () {
        helpers.init(this.callback);
      },
      "should respond with no error": function (err) {
        assert.isTrue(!err);
        if (initialized) {
          initialized();
        }
      }
    }
  };
};

helpers.requireHook = function (initialized) {
  return {
    "This test requires haibu.running.hook": {
      topic: function () {
        helpers.startHook(this.callback);
      },
      "should respond with no error": function (err, hook) {
        assert.isTrue(!err);
        if (initialized) {
          initialized();
        }
      }
    }
  }
}

helpers.requireStart = function (port, started) {
  return {
    "This test requires haibu.drone.start": {
      topic: function () {
        helpers.start(port, this.callback);
      },
      "should respond with no error": function (err, server) {
        assert.isTrue(!err);
        if (started) {
          started(server);
        }
      }
    }
  };
};

helpers.assertApp = function (message, assertFn) {
  var context = {
    topic: function (res, body) {
      var result = JSON.parse(body);
      request({
        uri: 'http://' + result.drone.host + ':' + result.drone.port
      }, this.callback);
    }
  };

  context[message] = assertFn;
  return context
}

helpers.assertTestApp = function () {
  return helpers.assertApp("should respond with 'hello, i know nodejitsu.'", function (err, res, body) {
    assert.equal(body, 'hello, i know nodejitsu.');
  });
};<commit_msg>[test minor] Print warning when parsing test-config.json fails<commit_after>/*
 * helpers.js: Test helpers for haibu
 *
 * (C) 2011 Nodejitsu Inc.
 * MIT LICENSE
 *
 */

var assert = require('assert'),
    exec = require('child_process').exec,
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    request = require('request'),
    haibu = require('../lib/haibu');

var helpers = exports,
    testConfig;

function showConfigWarning (requireAuth) {
  if ((!testConfig || !testConfig.auth ||
      testConfig.auth.username === 'test-username' ||
      testConfig.auth.apiKey === 'test-apiKey')
      && requireAuth) {
    console.warn("Config file test/fixtures/test-config.json doesn't have valid data. Skipping remote tests");
  }
}

helpers.loadConfig = function (requireAuth) {
  function showConfig () {
    showConfigWarning(requireAuth);
    return testConfig;
  }

  if (testConfig) {
    return showConfig();
  }

  try {
    var configFile = path.join(__dirname, 'fixtures', 'test-config.json'),
        config = JSON.parse(fs.readFileSync(configFile).toString());

    testConfig = config;
    return showConfig();
  }
  catch (ex) {
    console.warn('Error parsing test-config.json: ' + ex.message);
    return showConfig();
  }
};

helpers.cleanAutostart = function (callback) {
  exec('rm -rf autostart/*', callback);
};

helpers.init = function (callback) {
  var config = helpers.loadConfig() || {};
  helpers.cleanAutostart(function () {
    if (config) {
      Object.keys(config).forEach(function (key) {
        haibu.config.set(key, config[key]);
      });
    }

    haibu.init(callback);
  });
};

helpers.start = function (port, callback) {
  helpers.init(function (err) {
    haibu.drone.start({
      minUptime: 0,
      port: port,
      maxRestart: 2,
      init: false
    }, function (err, server) {
      return callback(err, server);
    });
  });
};

helpers.startHook = function (callback) {
  helpers.init(function (err) {
    haibu.drone.startHook(callback);
  });
}

helpers.requireInit = function (initialized) {
  return {
    "This test requires haibu.init": {
      topic: function () {
        helpers.init(this.callback);
      },
      "should respond with no error": function (err) {
        assert.isTrue(!err);
        if (initialized) {
          initialized();
        }
      }
    }
  };
};

helpers.requireHook = function (initialized) {
  return {
    "This test requires haibu.running.hook": {
      topic: function () {
        helpers.startHook(this.callback);
      },
      "should respond with no error": function (err, hook) {
        assert.isTrue(!err);
        if (initialized) {
          initialized();
        }
      }
    }
  }
}

helpers.requireStart = function (port, started) {
  return {
    "This test requires haibu.drone.start": {
      topic: function () {
        helpers.start(port, this.callback);
      },
      "should respond with no error": function (err, server) {
        assert.isTrue(!err);
        if (started) {
          started(server);
        }
      }
    }
  };
};

helpers.assertApp = function (message, assertFn) {
  var context = {
    topic: function (res, body) {
      var result = JSON.parse(body);
      request({
        uri: 'http://' + result.drone.host + ':' + result.drone.port
      }, this.callback);
    }
  };

  context[message] = assertFn;
  return context
}

helpers.assertTestApp = function () {
  return helpers.assertApp("should respond with 'hello, i know nodejitsu.'", function (err, res, body) {
    assert.equal(body, 'hello, i know nodejitsu.');
  });
};<|endoftext|>


<commit_before>import React from 'react';

import AddTodoForm from './AddTodoForm';
import TodoList from './TodoList';

export default class App extends React.Component {
  state = {
    todos: []
  }

  render() {
    return (

        <AddTodoForm onSubmit={this.handleAddTodo} />
        <TodoList todos={this.state.todos} />

    );
  }

  handleAddTodo = (newTodo) => {
    this.setState({
      todos: [...this.state.todos, newTodo]
    });
  }
}
<commit_msg>Change font for the whole app<commit_after>import React from 'react';

import AddTodoForm from './AddTodoForm';
import TodoList from './TodoList';

export default class App extends React.Component {
  state = {
    todos: []
  }

  render() {
    return (

        <AddTodoForm onSubmit={this.handleAddTodo} />
        <TodoList todos={this.state.todos} />

    );
  }

  handleAddTodo = (newTodo) => {
    this.setState({
      todos: [...this.state.todos, newTodo]
    });
  }
}

const style = {
  fontFamily: '"Roboto", Arial, sans-serif'
}
<|endoftext|>


<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var should = require('should');

var CLITest = require('../../../framework/csm-cli-test');
var testprefix = 'csm-cli-location-tests';

describe('csm', function () {
  describe('location', function () {
    var suite;

    before(function (done) {
      suite = new CLITest(testprefix);
      suite.setupSuite(done);
    });

    after(function (done) {
      suite.teardownSuite(done);
    });

    beforeEach(function (done) {
      suite.setupTest(done);
    });

    afterEach(function (done) {
      suite.teardownTest(done);
    });

    describe('list', function () {
      it('should work', function (done) {
        suite.execute('location list create %s --json', function (result) {
          result.exitStatus.should.equal(0);
          //verify the command indeed produces something valid such as a well known provider: websites
          var allResources = JSON.parse(result.text);
          allResources.some(function (res) {
            return res.name === 'Microsoft.Sql/servers';
          }).should.be.true;
          done();
        });
      });
    });
  });
});<commit_msg>test code fix<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var should = require('should');

var CLITest = require('../../../framework/csm-cli-test');
var testprefix = 'csm-cli-location-tests';

describe('csm', function () {
  describe('location', function () {
    var suite;

    before(function (done) {
      suite = new CLITest(testprefix);
      suite.setupSuite(done);
    });

    after(function (done) {
      suite.teardownSuite(done);
    });

    beforeEach(function (done) {
      suite.setupTest(done);
    });

    afterEach(function (done) {
      suite.teardownTest(done);
    });

    describe('list', function () {
      it('should work', function (done) {
        suite.execute('location list --json', function (result) {
          result.exitStatus.should.equal(0);
          //verify the command indeed produces something valid such as a well known provider: sql provider
          var allResources = JSON.parse(result.text);
          allResources.some(function (res) {
            return res.name === 'Microsoft.Sql/servers';
          }).should.be.true;
          done();
        });
      });
    });
  });
});<|endoftext|>


<commit_before>'use strict';
/**
 * Github FileBrowser configuration module.
 */

var m = require('mithril');
var $ = require('jquery');
var URI = require('URIjs');
var Fangorn = require('js/fangorn');
var waterbutler = require('js/waterbutler');

// Cross browser key codes for the Command key
var commandKeys = [224, 17, 91, 93];

function _uploadUrl(item, file) {
    return waterbutler.buildTreeBeardUpload(item, file, {branch: item.data.branch});
}

// TODO: Refactor, repeating from core function too much
function _removeEvent(event, items) {
    var tb = this;
    function cancelDelete() {
        tb.modal.dismiss();
    }
    function runDelete(item) {
        tb.select('.tb-modal-footer .text-danger').html(' Deleting...').css('color', 'grey');
        // delete from server, if successful delete from view
        $.ajax({
            url: waterbutler.buildTreeBeardDelete(item, {branch: item.data.branch, sha: item.data.extra.fileSha}),
            type : 'DELETE'
        })
            .done(function (data) {
                // delete view
                tb.deleteNode(item.parentID, item.id);
                Fangorn.Utils.dismissToolbar.call(tb);
                tb.modal.dismiss();
                tb.clearMultiselect();

            })
            .fail(function (data) {
                tb.modal.dismiss();
                Fangorn.Utils.dismissToolbar.call(tb);
                item.notify.update('Delete failed.', 'danger', undefined, 3000);
                tb.clearMultiselect();
            });
    }
    function runDeleteMultiple(items) {
        items.forEach(function (item) {
            runDelete(item);
        });
    }

    // If there is only one item being deleted, don't complicate the issue:
    if (items.length === 1) {
        var parent = items[0].parent();
        var mithrilContentSingle = m('div', [
            m('h3.break-word', 'Delete "' + items[0].data.name + '"'),
            m('p', 'This action is irreversible.'),
            parent.children.length < 2 ? m('p', 'If a folder in Github has no children it will automatically be removed.') : ''
        ]);
        var mithrilButtonsSingle = m('div', [
            m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function () { cancelDelete(); } }, 'Cancel'),
            m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function () { runDelete(items[0]); }  }, 'Delete')
        ]);
        // This is already being checked before this step but will keep this edit permission check
        if (items[0].data.permissions.edit) {
            tb.modal.update(mithrilContentSingle, mithrilButtonsSingle);
        }
    } else {
        // Check if all items can be deleted
        var canDelete = true;
        var deleteList = [];
        var noDeleteList = [];
        var mithrilContentMultiple;
        var mithrilButtonsMultiple;
        items.forEach(function (item, index, arr) {
            if (!item.data.permissions.edit) {
                canDelete = false;
                noDeleteList.push(item);
            } else {
                deleteList.push(item);
            }
        });
        // If all items can be deleted
        if (canDelete) {
            mithrilContentMultiple = m('div', [
                m('h3.break-word', 'Delete multiple files?'),
                m('p', 'This action is irreversible.'),
                deleteList.map(function (item) {
                    return m('.fangorn-canDelete.text-success', item.data.name);
                })
            ]);
            mithrilButtonsMultiple =  m('div', [
                m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function () { cancelDelete(); } }, 'Cancel'),
                m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function () { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete All')
            ]);
        } else {
            mithrilContentMultiple = m('div', [
                m('h3.break-word', 'Delete multiple files?'),
                m('p', 'Some of these files can\'t be deleted but you can delete the ones highlighted with green. This action is irreversible.'),
                deleteList.map(function (n) {
                    return m('.fangorn-canDelete.text-success', n.data.name);
                }),
                noDeleteList.map(function (n) {
                    return m('.fangorn-noDelete.text-warning', n.data.name);
                })
            ]);
            mithrilButtonsMultiple =  m('div', [
                m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function () { cancelDelete(); } }, 'Cancel'),
                m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function () { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete Some')
            ]);
        }
        tb.modal.update(mithrilContentMultiple, mithrilButtonsMultiple);
    }

    return true; // Let fangorn know this config option was used.
}

// Define Fangorn Button Actions
var _githubItemButtons = {
    view: function (ctrl, args, children) {
        var tb = args.treebeard;
        var item = args.item;
        var buttons = [];
        function _downloadEvent(event, item, col) {
            event.stopPropagation();
            window.location = waterbutler.buildTreeBeardDownload(item, {fileSha: item.data.extra.fileSha});
        }
        // Download Zip File
        if (item.kind === 'folder') {
            var branchArray = [];
            if (item.data.branches) {
                item.data.branch = item.data.branch || item.data.defaultBranch;
                for (var i = 0; i < item.data.branches.length; i++) {
                    var selected = item.data.branches[i] === item.data.branch ? 'selected' : '';
                    branchArray.push(m('option', {
                        selected: selected,
                        value: item.data.branches[i]
                    }, item.data.branches[i]));
                }
            }
            // If File and FileRead are not defined dropzone is not supported and neither is uploads
            if (window.File && window.FileReader && item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            Fangorn.ButtonEvents._uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            tb.toolbarMode('createFolder');
                        },
                        icon: 'fa fa-plus',
                        className: 'text-primary'
                    }, 'Create Folder')
                );
            }
            if (item.data.addonFullname) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.location = item.data.urls.zip;
                        },
                        icon: 'fa fa-download',
                        className: 'text-success'
                    }, 'Download'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.open(item.data.urls.repo, '_blank');
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, 'Open'),
                    m.component(Fangorn.Components.dropdown, {
                        'label': 'Branch: ',
                        onchange: function (event) {
                            changeBranch.call(tb, item, event.target.value);
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, branchArray)
                );
            }
        } else if (item.kind === 'file') {
            buttons.push(
                m.component(Fangorn.Components.button, {
                    onclick: function (event) {
                        _downloadEvent.call(tb, event, item);
                    },
                    icon: 'fa fa-download',
                    className: 'text-info'
                }, 'Download')
            );
            if (item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _removeEvent.call(tb, event, [item]);
                        },
                        icon: 'fa fa-trash',
                        className: 'text-danger'
                    }, 'Delete')
                );
            }
            if (item.data.permissions && item.data.permissions.view) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function(event) {
                            gotoFile.call(tb, item);
                        },
                        icon: 'fa fa-external-link',
                        className : 'text-info'
                    }, 'View'));

            }
        }

        return m('span', buttons); // Tell fangorn this function is used.
    }
}

function changeBranch(item, ref){
    item.data.branch = ref;
    this.updateFolder(null, item);
}

function _resolveLazyLoad(item) {
    return waterbutler.buildTreeBeardMetadata(item, {ref: item.data.branch});
}

function _fangornLazyLoadOnLoad (tree, event) {
    var tb = this;
    tree.children.forEach(function(item) {
        Fangorn.Utils.inheritFromParent(item, tree, ['branch']);
    });
    Fangorn.Utils.setCurrentFileID.call(tb, tree, window.contextVars.node.id, window.contextVars.file);
    if(!event){
        Fangorn.Utils.scrollToFile.call(tb, tb.currentFileID);
    }
}

function gotoFile (item) {
    var tb = this;
    var fileurl = new URI(item.data.nodeUrl)
        .segment('files')
        .segment(item.data.provider)
        .segment(item.data.path.substring(1))
        .search({branch: item.data.branch})
        .toString();
    if(commandKeys.indexOf(tb.pressedKey) !== -1) {
        window.open(fileurl, '_blank');
    } else {
        window.open(fileurl, '_self');
    }
}
function _fangornGithubTitle(item, col)  {
    var tb = this;
    if (item.data.addonFullname) {
        var branch = item.data.branch || item.data.defaultBranch;
        return m('span',[
            m('github-name', item.data.name + ' (' + branch + ')')
        ]);
    } else {
        if (item.kind === 'file' && item.data.permissions.view) {
            return m('span',[
                m('github-name.fg-file-links', {
                    onclick: function() {
                        gotoFile.call(tb, item);
                    }
                }, item.data.name)]);
        } else {
            return m('span', item.data.name);
        }
    }
}

function _fangornColumns (item) {
    var tb = this;
    var selectClass = '';
    var node = item.parent().parent();
    if (item.data.kind === 'file' && tb.currentFileID === item.id) {
        selectClass = 'fangorn-hover';
    }

    var columns = [];

    columns.push({
        data : 'name',
        folderIcons : true,
        filter: true,
        css: selectClass,
        custom : _fangornGithubTitle
    });

    if(tb.options.placement === 'project-files') {
        columns.push(
        {
            data  : 'downloads',
            filter : false,
            css : ''
        });
    }
    return columns;
}

function _fangornFolderIcons(item){
    if(item.data.iconUrl){
        return m('img',{src:item.data.iconUrl, style:{width:'16px', height:'auto'}}, ' ');
    }
    return undefined;
}

function _fangornUploadComplete(item){
    var index = this.returnIndex(item.id);
}

function _fangornUploadSuccess(file, item, response) {
    if (response) {
        response.branch = item.parent().data.branch;
    }
}

// Register configuration
Fangorn.config.github = {
    // Handle changing the branch select
    uploadUrl: _uploadUrl,
    lazyload: _resolveLazyLoad,
    resolveRows: _fangornColumns,
    folderIcon: _fangornFolderIcons,
    onUploadComplete: _fangornUploadComplete,
    lazyLoadOnLoad: _fangornLazyLoadOnLoad,
    uploadSuccess: _fangornUploadSuccess,
    itemButtons: _githubItemButtons,
    removeEvent : _removeEvent

};
<commit_msg>Update toolbar mode variable for create folder in github [skip ci]<commit_after>'use strict';
/**
 * Github FileBrowser configuration module.
 */

var m = require('mithril');
var $ = require('jquery');
var URI = require('URIjs');
var Fangorn = require('js/fangorn');
var waterbutler = require('js/waterbutler');

// Cross browser key codes for the Command key
var commandKeys = [224, 17, 91, 93];

function _uploadUrl(item, file) {
    return waterbutler.buildTreeBeardUpload(item, file, {branch: item.data.branch});
}

// TODO: Refactor, repeating from core function too much
function _removeEvent(event, items) {
    var tb = this;
    function cancelDelete() {
        tb.modal.dismiss();
    }
    function runDelete(item) {
        tb.select('.tb-modal-footer .text-danger').html(' Deleting...').css('color', 'grey');
        // delete from server, if successful delete from view
        $.ajax({
            url: waterbutler.buildTreeBeardDelete(item, {branch: item.data.branch, sha: item.data.extra.fileSha}),
            type : 'DELETE'
        })
            .done(function (data) {
                // delete view
                tb.deleteNode(item.parentID, item.id);
                Fangorn.Utils.dismissToolbar.call(tb);
                tb.modal.dismiss();
                tb.clearMultiselect();

            })
            .fail(function (data) {
                tb.modal.dismiss();
                Fangorn.Utils.dismissToolbar.call(tb);
                item.notify.update('Delete failed.', 'danger', undefined, 3000);
                tb.clearMultiselect();
            });
    }
    function runDeleteMultiple(items) {
        items.forEach(function (item) {
            runDelete(item);
        });
    }

    // If there is only one item being deleted, don't complicate the issue:
    if (items.length === 1) {
        var parent = items[0].parent();
        var mithrilContentSingle = m('div', [
            m('h3.break-word', 'Delete "' + items[0].data.name + '"'),
            m('p', 'This action is irreversible.'),
            parent.children.length < 2 ? m('p', 'If a folder in Github has no children it will automatically be removed.') : ''
        ]);
        var mithrilButtonsSingle = m('div', [
            m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function () { cancelDelete(); } }, 'Cancel'),
            m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function () { runDelete(items[0]); }  }, 'Delete')
        ]);
        // This is already being checked before this step but will keep this edit permission check
        if (items[0].data.permissions.edit) {
            tb.modal.update(mithrilContentSingle, mithrilButtonsSingle);
        }
    } else {
        // Check if all items can be deleted
        var canDelete = true;
        var deleteList = [];
        var noDeleteList = [];
        var mithrilContentMultiple;
        var mithrilButtonsMultiple;
        items.forEach(function (item, index, arr) {
            if (!item.data.permissions.edit) {
                canDelete = false;
                noDeleteList.push(item);
            } else {
                deleteList.push(item);
            }
        });
        // If all items can be deleted
        if (canDelete) {
            mithrilContentMultiple = m('div', [
                m('h3.break-word', 'Delete multiple files?'),
                m('p', 'This action is irreversible.'),
                deleteList.map(function (item) {
                    return m('.fangorn-canDelete.text-success', item.data.name);
                })
            ]);
            mithrilButtonsMultiple =  m('div', [
                m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function () { cancelDelete(); } }, 'Cancel'),
                m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function () { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete All')
            ]);
        } else {
            mithrilContentMultiple = m('div', [
                m('h3.break-word', 'Delete multiple files?'),
                m('p', 'Some of these files can\'t be deleted but you can delete the ones highlighted with green. This action is irreversible.'),
                deleteList.map(function (n) {
                    return m('.fangorn-canDelete.text-success', n.data.name);
                }),
                noDeleteList.map(function (n) {
                    return m('.fangorn-noDelete.text-warning', n.data.name);
                })
            ]);
            mithrilButtonsMultiple =  m('div', [
                m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function () { cancelDelete(); } }, 'Cancel'),
                m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function () { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete Some')
            ]);
        }
        tb.modal.update(mithrilContentMultiple, mithrilButtonsMultiple);
    }

    return true; // Let fangorn know this config option was used.
}

// Define Fangorn Button Actions
var _githubItemButtons = {
    view: function (ctrl, args, children) {
        var tb = args.treebeard;
        var item = args.item;
        var buttons = [];
        function _downloadEvent(event, item, col) {
            event.stopPropagation();
            window.location = waterbutler.buildTreeBeardDownload(item, {fileSha: item.data.extra.fileSha});
        }
        // Download Zip File
        if (item.kind === 'folder') {
            var branchArray = [];
            if (item.data.branches) {
                item.data.branch = item.data.branch || item.data.defaultBranch;
                for (var i = 0; i < item.data.branches.length; i++) {
                    var selected = item.data.branches[i] === item.data.branch ? 'selected' : '';
                    branchArray.push(m('option', {
                        selected: selected,
                        value: item.data.branches[i]
                    }, item.data.branches[i]));
                }
            }
            // If File and FileRead are not defined dropzone is not supported and neither is uploads
            if (window.File && window.FileReader && item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            Fangorn.ButtonEvents._uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            tb.toolbarMode(Fangorn.Components.toolbarModes.ADDFOLDER);
                        },
                        icon: 'fa fa-plus',
                        className: 'text-primary'
                    }, 'Create Folder')
                );
            }
            if (item.data.addonFullname) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.location = item.data.urls.zip;
                        },
                        icon: 'fa fa-download',
                        className: 'text-success'
                    }, 'Download'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.open(item.data.urls.repo, '_blank');
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, 'Open'),
                    m.component(Fangorn.Components.dropdown, {
                        'label': 'Branch: ',
                        onchange: function (event) {
                            changeBranch.call(tb, item, event.target.value);
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, branchArray)
                );
            }
        } else if (item.kind === 'file') {
            buttons.push(
                m.component(Fangorn.Components.button, {
                    onclick: function (event) {
                        _downloadEvent.call(tb, event, item);
                    },
                    icon: 'fa fa-download',
                    className: 'text-info'
                }, 'Download')
            );
            if (item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _removeEvent.call(tb, event, [item]);
                        },
                        icon: 'fa fa-trash',
                        className: 'text-danger'
                    }, 'Delete')
                );
            }
            if (item.data.permissions && item.data.permissions.view) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function(event) {
                            gotoFile.call(tb, item);
                        },
                        icon: 'fa fa-external-link',
                        className : 'text-info'
                    }, 'View'));

            }
        }

        return m('span', buttons); // Tell fangorn this function is used.
    }
}

function changeBranch(item, ref){
    item.data.branch = ref;
    this.updateFolder(null, item);
}

function _resolveLazyLoad(item) {
    return waterbutler.buildTreeBeardMetadata(item, {ref: item.data.branch});
}

function _fangornLazyLoadOnLoad (tree, event) {
    var tb = this;
    tree.children.forEach(function(item) {
        Fangorn.Utils.inheritFromParent(item, tree, ['branch']);
    });
    Fangorn.Utils.setCurrentFileID.call(tb, tree, window.contextVars.node.id, window.contextVars.file);
    if(!event){
        Fangorn.Utils.scrollToFile.call(tb, tb.currentFileID);
    }
}

function gotoFile (item) {
    var tb = this;
    var fileurl = new URI(item.data.nodeUrl)
        .segment('files')
        .segment(item.data.provider)
        .segment(item.data.path.substring(1))
        .search({branch: item.data.branch})
        .toString();
    if(commandKeys.indexOf(tb.pressedKey) !== -1) {
        window.open(fileurl, '_blank');
    } else {
        window.open(fileurl, '_self');
    }
}
function _fangornGithubTitle(item, col)  {
    var tb = this;
    if (item.data.addonFullname) {
        var branch = item.data.branch || item.data.defaultBranch;
        return m('span',[
            m('github-name', item.data.name + ' (' + branch + ')')
        ]);
    } else {
        if (item.kind === 'file' && item.data.permissions.view) {
            return m('span',[
                m('github-name.fg-file-links', {
                    onclick: function() {
                        gotoFile.call(tb, item);
                    }
                }, item.data.name)]);
        } else {
            return m('span', item.data.name);
        }
    }
}

function _fangornColumns (item) {
    var tb = this;
    var selectClass = '';
    var node = item.parent().parent();
    if (item.data.kind === 'file' && tb.currentFileID === item.id) {
        selectClass = 'fangorn-hover';
    }

    var columns = [];

    columns.push({
        data : 'name',
        folderIcons : true,
        filter: true,
        css: selectClass,
        custom : _fangornGithubTitle
    });

    if(tb.options.placement === 'project-files') {
        columns.push(
        {
            data  : 'downloads',
            filter : false,
            css : ''
        });
    }
    return columns;
}

function _fangornFolderIcons(item){
    if(item.data.iconUrl){
        return m('img',{src:item.data.iconUrl, style:{width:'16px', height:'auto'}}, ' ');
    }
    return undefined;
}

function _fangornUploadComplete(item){
    var index = this.returnIndex(item.id);
}

function _fangornUploadSuccess(file, item, response) {
    if (response) {
        response.branch = item.parent().data.branch;
    }
}

// Register configuration
Fangorn.config.github = {
    // Handle changing the branch select
    uploadUrl: _uploadUrl,
    lazyload: _resolveLazyLoad,
    resolveRows: _fangornColumns,
    folderIcon: _fangornFolderIcons,
    onUploadComplete: _fangornUploadComplete,
    lazyLoadOnLoad: _fangornLazyLoadOnLoad,
    uploadSuccess: _fangornUploadSuccess,
    itemButtons: _githubItemButtons,
    removeEvent : _removeEvent

};
<|endoftext|>


<commit_before>/**
 * jquery-textrange
 * A jQuery plugin for getting, setting and replacing the selected text in input fields and textareas.
 * See the [wiki](https://github.com/dwieeb/jquery-textrange/wiki) for usage and examples.
 *
 * (c) 2013 Daniel Imhoff <dwieeb@gmail.com> - danielimhoff.com
 */
(function($) {

	var browserType,

	textrange = {

		/**
		 * $().textrange() or $().textrange('get')
		 *
		 * Retrieves an object containing the start and end location of the text range, the length of the range and the
		 * substring of the range.
		 *
		 * @param (optional) property
		 * @return An object of properties including position, start, end, length, and text or a specific property.
		 */
		get: function(property) {
			return _textrange[browserType].get.apply(this, [property]);
		},

		/**
		 * $().textrange('set')
		 *
		 * Sets the selected text of an object by specifying the start and length of the selection.
		 *
		 * The start and length parameters are identical to PHP's substr() function with the following changes:
		 *  - excluding start will select all the text in the field.
		 *  - passing 0 for length will set the cursor at start. See $().textrange('setcursor')
		 *
		 * @param (optional) start
		 * @param (optional) length
		 *
		 * @see http://php.net/manual/en/function.substr.php
		 */
		set: function(start, length) {
			var s = parseInt(start),
			    l = parseInt(length),
			    e;

			if (typeof start === 'undefined') {
				s = 0;
			}
			else if (start < 0) {
				s = this.val().length + s;
			}

			if (typeof length === 'undefined') {
				e = this.val().length;
			}
			else if (length >= 0) {
				e = s + l;
			}
			else {
				e = this.val().length + l;
			}

			_textrange[browserType].set.apply(this, [s, e]);

			return this;
		},

		/**
		 * $().textrange('setcursor')
		 *
		 * Sets the cursor at a position of the text field.
		 *
		 * @param position
		 */
		setcursor: function(position) {
			return this.textrange('set', position, 0);
		},

		/**
		 * $().textrange('replace')
		 * Replaces the selected text in the input field or textarea with text.
		 *
		 * @param text The text to replace the selection with.
		 */
		replace: function(text) {
			_textrange[browserType].replace.apply(this, [text]);

			return this;
		},

		/**
		 * Alias for $().textrange('replace')
		 */
		insert: function(text) {
			return this.textrange('replace', text);
		}
	},

	_textrange = {
		xul: {
			get: function(property) {
				var props = {
					position: this[0].selectionStart,
					start: this[0].selectionStart,
					end: this[0].selectionEnd,
					length: this[0].selectionEnd - this[0].selectionStart,
					text: this.val().substring(this[0].selectionStart, this[0].selectionEnd)
				};

				return typeof property === 'undefined' ? props : props[property];
			},

			set: function(start, end) {
				this[0].selectionStart = start;
				this[0].selectionEnd = end;
			},

			replace: function(text) {
				var start = this[0].selectionStart;
				this.val(this.val().substring(0, this[0].selectionStart) + text + this.val().substring(this[0].selectionEnd, this.val().length));
				this[0].selectionStart = start;
				this[0].selectionEnd = start + text.length;
			}
		},

		msie: {
			get: function(property) {
				var range = document.selection.createRange();

				if (typeof range === 'undefined') {
					return {
						position: 0,
						start: 0,
						end: this[0].val().length,
						length: this[0].val().length,
						text: this.val()
					};
				}

				var rangetext = this[0].createTextRange();
				var rangetextcopy = rangetext.duplicate();

				rangetext.moveToBookmark(range.getBookmark());
				rangetextcopy.setEndPoint('EndToStart', rangetext);

				var props = {
					position: rangetextcopy.text.length,
					start: rangetextcopy.text.length,
					end: rangetextcopy.text.length + range.text.length,
					length: range.text.length,
					text: range.text
				};

				return typeof property === 'undefined' ? props : props[property];
			},

			set: function(start, end) {
				var range = this[0].createTextRange();

				if (typeof range === 'undefined') {
					return this;
				}

				if (typeof start !== 'undefined') {
					range.moveStart('character', start);
					range.collapse();
				}

				if (typeof end !== 'undefined') {
					range.moveEnd('character', end - start);
				}

				range.select();
			},

			replace: function(text) {
				document.selection.createRange().text = text;
			}
		}
	};

	$.fn.textrange = function(method) {
		if (typeof browserType === 'undefined') {
			browserType = 'selectionStart' in this[0] ? 'xul' : document.selection ? 'msie' : 'unknown';
		}

		// I don't know how to support this browser. :c
		if (browserType === 'unknown') {
			return this;
		}

		// Prevents unpleasant behaviour for textareas in IE:
		// If you have a textarea which is too wide to be displayed entirely and therfore has to be scrolled horizontally,
		// then typing one character after another will scroll the page automatically to the right at the moment you reach
		// the right border of the visible part. But calling the focus function causes the page to be scrolled to the left
		// edge of the textarea. Immediately after that jump to the left side, the content is scrolled back to the cursor
		// position, which leads to a flicker page every time you type a character.
		if (document.activeElement !== this[0]) {
			this[0].focus();
		}

		if (typeof method === 'undefined' || typeof method !== 'string') {
			return textrange.get.apply(this);
		}
		else if (typeof textrange[method] === 'function') {
			return textrange[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else {
			$.error("Method " + method + " does not exist in jQuery.textrange");
		}
	};
})(jQuery);
<commit_msg>achieves #10<commit_after>/**
 * jquery-textrange
 * A jQuery plugin for getting, setting and replacing the selected text in input fields and textareas.
 * See the [wiki](https://github.com/dwieeb/jquery-textrange/wiki) for usage and examples.
 *
 * (c) 2013 Daniel Imhoff <dwieeb@gmail.com> - danielimhoff.com
 */
(function(factory) {
	if (typeof define === 'function' && define.amd) {
		define(['jquery'], factory);
	} else {
		factory(jQuery);
	}
})(function($) {

	var browserType,

	textrange = {

		/**
		 * $().textrange() or $().textrange('get')
		 *
		 * Retrieves an object containing the start and end location of the text range, the length of the range and the
		 * substring of the range.
		 *
		 * @param (optional) property
		 * @return An object of properties including position, start, end, length, and text or a specific property.
		 */
		get: function(property) {
			return _textrange[browserType].get.apply(this, [property]);
		},

		/**
		 * $().textrange('set')
		 *
		 * Sets the selected text of an object by specifying the start and length of the selection.
		 *
		 * The start and length parameters are identical to PHP's substr() function with the following changes:
		 *  - excluding start will select all the text in the field.
		 *  - passing 0 for length will set the cursor at start. See $().textrange('setcursor')
		 *
		 * @param (optional) start
		 * @param (optional) length
		 *
		 * @see http://php.net/manual/en/function.substr.php
		 */
		set: function(start, length) {
			var s = parseInt(start),
			    l = parseInt(length),
			    e;

			if (typeof start === 'undefined') {
				s = 0;
			}
			else if (start < 0) {
				s = this.val().length + s;
			}

			if (typeof length === 'undefined') {
				e = this.val().length;
			}
			else if (length >= 0) {
				e = s + l;
			}
			else {
				e = this.val().length + l;
			}

			_textrange[browserType].set.apply(this, [s, e]);

			return this;
		},

		/**
		 * $().textrange('setcursor')
		 *
		 * Sets the cursor at a position of the text field.
		 *
		 * @param position
		 */
		setcursor: function(position) {
			return this.textrange('set', position, 0);
		},

		/**
		 * $().textrange('replace')
		 * Replaces the selected text in the input field or textarea with text.
		 *
		 * @param text The text to replace the selection with.
		 */
		replace: function(text) {
			_textrange[browserType].replace.apply(this, [text]);

			return this;
		},

		/**
		 * Alias for $().textrange('replace')
		 */
		insert: function(text) {
			return this.textrange('replace', text);
		}
	},

	_textrange = {
		xul: {
			get: function(property) {
				var props = {
					position: this[0].selectionStart,
					start: this[0].selectionStart,
					end: this[0].selectionEnd,
					length: this[0].selectionEnd - this[0].selectionStart,
					text: this.val().substring(this[0].selectionStart, this[0].selectionEnd)
				};

				return typeof property === 'undefined' ? props : props[property];
			},

			set: function(start, end) {
				this[0].selectionStart = start;
				this[0].selectionEnd = end;
			},

			replace: function(text) {
				var start = this[0].selectionStart;
				this.val(this.val().substring(0, this[0].selectionStart) + text + this.val().substring(this[0].selectionEnd, this.val().length));
				this[0].selectionStart = start;
				this[0].selectionEnd = start + text.length;
			}
		},

		msie: {
			get: function(property) {
				var range = document.selection.createRange();

				if (typeof range === 'undefined') {
					return {
						position: 0,
						start: 0,
						end: this[0].val().length,
						length: this[0].val().length,
						text: this.val()
					};
				}

				var rangetext = this[0].createTextRange();
				var rangetextcopy = rangetext.duplicate();

				rangetext.moveToBookmark(range.getBookmark());
				rangetextcopy.setEndPoint('EndToStart', rangetext);

				var props = {
					position: rangetextcopy.text.length,
					start: rangetextcopy.text.length,
					end: rangetextcopy.text.length + range.text.length,
					length: range.text.length,
					text: range.text
				};

				return typeof property === 'undefined' ? props : props[property];
			},

			set: function(start, end) {
				var range = this[0].createTextRange();

				if (typeof range === 'undefined') {
					return this;
				}

				if (typeof start !== 'undefined') {
					range.moveStart('character', start);
					range.collapse();
				}

				if (typeof end !== 'undefined') {
					range.moveEnd('character', end - start);
				}

				range.select();
			},

			replace: function(text) {
				document.selection.createRange().text = text;
			}
		}
	};

	$.fn.textrange = function(method) {
		if (typeof browserType === 'undefined') {
			browserType = 'selectionStart' in this[0] ? 'xul' : document.selection ? 'msie' : 'unknown';
		}

		// I don't know how to support this browser. :c
		if (browserType === 'unknown') {
			return this;
		}

		// Prevents unpleasant behaviour for textareas in IE:
		// If you have a textarea which is too wide to be displayed entirely and therfore has to be scrolled horizontally,
		// then typing one character after another will scroll the page automatically to the right at the moment you reach
		// the right border of the visible part. But calling the focus function causes the page to be scrolled to the left
		// edge of the textarea. Immediately after that jump to the left side, the content is scrolled back to the cursor
		// position, which leads to a flicker page every time you type a character.
		if (document.activeElement !== this[0]) {
			this[0].focus();
		}

		if (typeof method === 'undefined' || typeof method !== 'string') {
			return textrange.get.apply(this);
		}
		else if (typeof textrange[method] === 'function') {
			return textrange[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else {
			$.error("Method " + method + " does not exist in jQuery.textrange");
		}
	};
});
<|endoftext|>


<commit_before>function viewCollectionDetails(collectionId) {

  getCollectionDetails(collectionId,
    success = function (response) {
      populateCollectionDetails(response, collectionId);
    },
    error = function (response) {
      handleApiError(response);
    }
  );

  function populateCollectionDetails(collection, collectionId) {

    Florence.setActiveCollection(collection);

    if (!collection.publishDate) {
      collection.date = '[manual collection]';
    } else {
      collection.date = StringUtils.formatIsoFull(collection.publishDate);
    }

    ProcessPages(collection.inProgress);
    ProcessPages(collection.complete);
    ProcessPages(collection.reviewed);

    var collectionHtml = window.templates.collectionDetails(collection);
    $('.collection-selected').html(collectionHtml).animate({right: "0%"}, 500);

    var deleteButton = $('#collection-delete');
    if (collection.inProgress.length === 0
      && collection.complete.length === 0
      && collection.reviewed.length === 0) {
        deleteButton.show().click(function () {
          var result = confirm("Are you sure you want to delete this collection?");
          if (result === true) {
            deleteCollection(collectionId,
            function () {
            alert('Collection deleted');
            viewCollections();
            },
            function (error) {
              viewCollectionDetails(collectionId);
              alert(error + ' File has not been deleted. Contact an administrator');
            })
          } else {}
        });
      } else {
        deleteButton.hide();
      }

    var approve = $('.btn-collection-approve');
    if (collection.inProgress.length === 0
      && collection.complete.length === 0
      && collection.reviewed.length > 0) {
      approve.show().click(function () {
        $('.js').prepend(
          "
" +
          "
" +
          "

" +
          "

" +
          "
" +
          "
");
        postApproveCollection(collection.id);
      });
    }
    else {
      // You can't approve collections unless there is nothing left to be reviewed
      approve.hide();
    }

    //page-list
    $('.page-item').click(function () {
      $('.page-list li').removeClass('selected');
      $('.page-options').hide();

      $(this).parent('li').addClass('selected');
      $(this).next('.page-options').show();
    });

    $('.btn-page-edit').click(function () {
      var path = $(this).attr('data-path');
      var language = $(this).attr('data-language');
      if (language === 'cy') {
        var safePath = checkPathSlashes(path);
        Florence.globalVars.welsh = true;
      } else {
        var safePath = checkPathSlashes(path);
        Florence.globalVars.welsh = false;
      }
      getPageDataDescription(collectionId, safePath,
        success = function () {
          createWorkspace(safePath, collectionId, 'edit');
        },
        error = function () {
          alert('uri: ' + safePath + ' is not found.');
        }
      );
    });

    $('.page-delete').click(function () {
      var path = $(this).attr('data-path');
      var language = $(this).attr('data-language');
      if (path.match(/\/bulletins\//) || path.match(/\/articles\//)) {
        var result = confirm("This will delete the English and Welsh content of this page, if any. Are you sure you" +
          " want to delete this page from the collection?");
      } else if (language === 'cy') {
        var result = confirm("Are you sure you want to delete this page from the collection?");
      } else {
        var result = confirm("This will delete the English and Welsh content of this page, if any. Are you sure you" +
          " want to delete this page from the collection?");
      }
      if (result === true) {
        if (language === 'cy' && !(path.match(/\/bulletins\//) || path.match(/\/articles\//))) {
          path = path + '/data_cy.json';
        }
        deleteContent(collectionId, path, function() {
            viewCollectionDetails(collectionId);
            alert('File deleted');
          }, function(error) {
            viewCollectionDetails(collectionId);
            alert(error + ' File has not been deleted. Contact an administrator');
          }
        );
      }
    });

    $('.collection-selected .btn-collection-cancel').click(function () {
      $('.collection-selected').stop().animate({right: "-50%"}, 500);
      $('.collections-select-table tbody tr').removeClass('selected');
      // Wait until the animation ends
      setTimeout(function () {
        viewController('collections');
      }, 500);
    });

    $('.btn-collection-work-on').click(function () {
      Florence.globalVars.welsh = false;
      createWorkspace('', collectionId, 'browse');
    });

    setCollectionDetailsHeight();
  }

  function ProcessPages(pages) {
    _.sortBy(pages, 'uri');
    _.each(pages, function (page) {
      page.uri = page.uri.replace('/data.json', '');
      return page;
    });
  }

  function setCollectionDetailsHeight(){
    var panelHeight = parseInt($('.collection-selected').height());

    var headHeight = parseInt($('.section-head').height());
    var headPadding = parseInt($('.section-head').css('padding-bottom'));

    var contentPadding = parseInt($('.section-content').css('padding-bottom'));

    var navHeight = parseInt($('.section-nav').height());
    var navPadding = (parseInt($('.section-nav').css('padding-bottom')))+(parseInt($('.section-nav').css('padding-top')));

    var contentHeight = panelHeight-(headHeight+headPadding+contentPadding+navHeight+navPadding);
    $('.section-content').css('height', contentHeight);
  }
}<commit_msg>Logout the user if trying to access pages in a collection.<commit_after>function viewCollectionDetails(collectionId) {

  getCollectionDetails(collectionId,
    success = function (response) {
      populateCollectionDetails(response, collectionId);
    },
    error = function (response) {
      handleApiError(response);
    }
  );

  function populateCollectionDetails(collection, collectionId) {

    Florence.setActiveCollection(collection);

    if (!collection.publishDate) {
      collection.date = '[manual collection]';
    } else {
      collection.date = StringUtils.formatIsoFull(collection.publishDate);
    }

    ProcessPages(collection.inProgress);
    ProcessPages(collection.complete);
    ProcessPages(collection.reviewed);

    var collectionHtml = window.templates.collectionDetails(collection);
    $('.collection-selected').html(collectionHtml).animate({right: "0%"}, 500);

    var deleteButton = $('#collection-delete');
    if (collection.inProgress.length === 0
      && collection.complete.length === 0
      && collection.reviewed.length === 0) {
        deleteButton.show().click(function () {
          var result = confirm("Are you sure you want to delete this collection?");
          if (result === true) {
            deleteCollection(collectionId,
            function () {
            alert('Collection deleted');
            viewCollections();
            },
            function (error) {
              viewCollectionDetails(collectionId);
              alert(error + ' File has not been deleted. Contact an administrator');
            })
          } else {}
        });
      } else {
        deleteButton.hide();
      }

    var approve = $('.btn-collection-approve');
    if (collection.inProgress.length === 0
      && collection.complete.length === 0
      && collection.reviewed.length > 0) {
      approve.show().click(function () {
        $('.js').prepend(
          "
" +
          "
" +
          "

" +
          "

" +
          "
" +
          "
");
        postApproveCollection(collection.id);
      });
    }
    else {
      // You can't approve collections unless there is nothing left to be reviewed
      approve.hide();
    }

    //page-list
    $('.page-item').click(function () {
      $('.page-list li').removeClass('selected');
      $('.page-options').hide();

      $(this).parent('li').addClass('selected');
      $(this).next('.page-options').show();
    });

    $('.btn-page-edit').click(function () {
      var path = $(this).attr('data-path');
      var language = $(this).attr('data-language');
      if (language === 'cy') {
        var safePath = checkPathSlashes(path);
        Florence.globalVars.welsh = true;
      } else {
        var safePath = checkPathSlashes(path);
        Florence.globalVars.welsh = false;
      }
      getPageDataDescription(collectionId, safePath,
        success = function () {
          createWorkspace(safePath, collectionId, 'edit');
        },
        error = function (response) {
          handleApiError(response);
        }
      );
    });

    $('.page-delete').click(function () {
      var path = $(this).attr('data-path');
      var language = $(this).attr('data-language');
      if (path.match(/\/bulletins\//) || path.match(/\/articles\//)) {
        var result = confirm("This will delete the English and Welsh content of this page, if any. Are you sure you" +
          " want to delete this page from the collection?");
      } else if (language === 'cy') {
        var result = confirm("Are you sure you want to delete this page from the collection?");
      } else {
        var result = confirm("This will delete the English and Welsh content of this page, if any. Are you sure you" +
          " want to delete this page from the collection?");
      }
      if (result === true) {
        if (language === 'cy' && !(path.match(/\/bulletins\//) || path.match(/\/articles\//))) {
          path = path + '/data_cy.json';
        }
        deleteContent(collectionId, path, function() {
            viewCollectionDetails(collectionId);
            alert('File deleted');
          }, function(error) {
            handleApiError(error);
          }
        );
      }
    });

    $('.collection-selected .btn-collection-cancel').click(function () {
      $('.collection-selected').stop().animate({right: "-50%"}, 500);
      $('.collections-select-table tbody tr').removeClass('selected');
      // Wait until the animation ends
      setTimeout(function () {
        viewController('collections');
      }, 500);
    });

    $('.btn-collection-work-on').click(function () {
      Florence.globalVars.welsh = false;
      createWorkspace('', collectionId, 'browse');
    });

    setCollectionDetailsHeight();
  }

  function ProcessPages(pages) {
    _.sortBy(pages, 'uri');
    _.each(pages, function (page) {
      page.uri = page.uri.replace('/data.json', '');
      return page;
    });
  }

  function setCollectionDetailsHeight(){
    var panelHeight = parseInt($('.collection-selected').height());

    var headHeight = parseInt($('.section-head').height());
    var headPadding = parseInt($('.section-head').css('padding-bottom'));

    var contentPadding = parseInt($('.section-content').css('padding-bottom'));

    var navHeight = parseInt($('.section-nav').height());
    var navPadding = (parseInt($('.section-nav').css('padding-bottom')))+(parseInt($('.section-nav').css('padding-top')));

    var contentHeight = panelHeight-(headHeight+headPadding+contentPadding+navHeight+navPadding);
    $('.section-content').css('height', contentHeight);
  }
}<|endoftext|>


<commit_before>var url = require('url');
var https = require('https');
var http = require('http');
var util = require('../util');
var config = util.config;

function request(req, res, port) {
	var options = url.parse(util.getFullUrl(req));
	options.host = '127.0.0.1';
	options.method = req.method;
	options.hostname = null;
	options.protocol = null;
	if (port) {
		options.port = port;
	}
	req.headers['x-forwarded-for'] = util.getClientIp(req);
	options.headers = req.headers;
	req.pipe(http.request(options, function(_res) {
		res.writeHead(_res.statusCode || 0, _res.headers);
		res.src(_res);
	}));
}

module.exports = function(req, res, next) {
	var host = req.headers.host;
	if (host == config.localUIHost) {
		request(req, res, /^\/data\b/.test() ? config.uidataport : config.uiport);
	} else if (host == config.WEINRE_HOST) {
		request(req, res, config.weinreport);
	} else {
		next();
	}
};
<commit_msg>data ui<commit_after>var url = require('url');
var https = require('https');
var http = require('http');
var util = require('../util');
var config = util.config;

function request(req, res, port) {
	var options = url.parse(util.getFullUrl(req));
	options.host = '127.0.0.1';
	options.method = req.method;
	options.hostname = null;
	options.protocol = null;
	if (port) {
		options.port = port;
	}
	req.headers['x-forwarded-for'] = util.getClientIp(req);
	options.headers = req.headers;
	req.pipe(http.request(options, function(_res) {
		res.writeHead(_res.statusCode || 0, _res.headers);
		res.src(_res);
	}));
}

module.exports = function(req, res, next) {
	var host = req.headers.host;
	if (host == config.localUIHost) {
		var path = req.url;
		if (util.hasProtocol(path)) {
			path = url.parse(path).path;
		}
		request(req, res, /^\/data\b/.test(path) ? config.uidataport : config.uiport);
	} else if (host == config.WEINRE_HOST) {
		request(req, res, config.weinreport);
	} else {
		next();
	}
};
<|endoftext|>


<commit_before>var backends = require('../lib/backends');
var Model = require('ampersand-model');
var Collection = require('ampersand-rest-collection');
var async = require('async');
var debug = require('debug')('mongodb-storage-mixin:test:helpers');

/**
 * Helper to clear all given namespaces for a backend.
 * @param  {String}   backendName   name of the backend, e.g. `local`
 * @param  {Array}   namespaces     array of namespace strings
 * @param  {Function} done          errback
 */
var clearNamespaces = function(backendName, namespaces, done) {
  debug('Clearing namespaces for backend %s', backendName, namespaces);
  var tasks = namespaces.map(function(namespace) {
    var backend = backends[backendName];
    return function(cb) {
      backend.clear(namespace, cb);
    };
  });
  async.parallel(tasks, function(err, res) {
    debug('Namespaces cleared for backend %s', backendName);
    done(err, res);
  });
};

var Spaceship = Model.extend({
  idAttribute: 'name',
  namespace: 'Spaceships',
  props: {
    name: {
      type: 'string',
      required: true
    },
    enableJetpack: {
      type: 'boolean',
      default: false,
      required: true
    },
    warpSpeed: {
      type: 'number',
      default: 1,
      required: true
    }
  }
});

var Fleet = Collection.extend({
  namespace: 'Spaceships',
  model: Spaceship
});

var Planet = Model.extend({
  idAttribute: 'name',
  namespace: 'Planets',
  props: {
    name: ['string', true, ''],
    population: ['number', true, 0]
  }
});

var User = Model.extend({
  idAttribute: 'id',
  namespace: 'Users',
  props: {
    id: {
      type: 'string',
      required: true
    },
    name: {
      type: 'string',
      required: true
    },
    email: {
      type: 'string',
      required: true
    },
    password: {
      type: 'string',
      required: true
    }
  }
});

var Users = Collection.extend({
  namespace: 'Users',
  model: User
});

module.exports = {
  clearNamespaces: clearNamespaces,
  Spaceship: Spaceship,
  Fleet: Fleet,
  Planet: Planet,
  User: User,
  Users: Users
};
<commit_msg>Its something in this helper function...<commit_after>var backends = require('../lib/backends');
var Model = require('ampersand-model');
var Collection = require('ampersand-rest-collection');
var async = require('async');
var debug = require('debug')('mongodb-storage-mixin:test:helpers');

/**
 * Helper to clear all given namespaces for a backend.
 * @param  {String}   backendName   name of the backend, e.g. `local`
 * @param  {Array}   namespaces     array of namespace strings
 * @param  {Function} done          errback
 */
var clearNamespaces = function(backendName, namespaces, done) {
  debug('Clearing namespaces for backend %s', backendName, namespaces);
  var tasks = namespaces.map(function(namespace) {
    var backend = backends[backendName];
    return function(cb) {
      backend.clear(namespace, cb);
    };
  });
  async.parallel(tasks, function(err, res) {
    if (err) {
      console.error('Error clearing namespaces', err);
      return done(err);
    }
    debug('Namespaces cleared for backend %s', backendName);
    done(err, res);
  });
};

var Spaceship = Model.extend({
  idAttribute: 'name',
  namespace: 'Spaceships',
  props: {
    name: {
      type: 'string',
      required: true
    },
    enableJetpack: {
      type: 'boolean',
      default: false,
      required: true
    },
    warpSpeed: {
      type: 'number',
      default: 1,
      required: true
    }
  }
});

var Fleet = Collection.extend({
  namespace: 'Spaceships',
  model: Spaceship
});

var Planet = Model.extend({
  idAttribute: 'name',
  namespace: 'Planets',
  props: {
    name: ['string', true, ''],
    population: ['number', true, 0]
  }
});

var User = Model.extend({
  idAttribute: 'id',
  namespace: 'Users',
  props: {
    id: {
      type: 'string',
      required: true
    },
    name: {
      type: 'string',
      required: true
    },
    email: {
      type: 'string',
      required: true
    },
    password: {
      type: 'string',
      required: true
    }
  }
});

var Users = Collection.extend({
  namespace: 'Users',
  model: User
});

module.exports = {
  clearNamespaces: clearNamespaces,
  Spaceship: Spaceship,
  Fleet: Fleet,
  Planet: Planet,
  User: User,
  Users: Users
};
<|endoftext|>


<commit_before>(function(module) {
    module.directive('setupDetails', setupDetailsDirective);
    function setupDetailsDirective() {
        return {
            restrict: 'E',
            scope: {
                setup: '='
            },
            //controller: 'DetailTabsDirectiveController',
            //controllerAs: 'ctrl',
            //bindToController: true,
            templateUrl: 'application/directives/partials/setup-details.html'
        };
    }
}(angular.module('materialscommons')));
<commit_msg>Remove commented out code.<commit_after>(function(module) {
    module.directive('setupDetails', setupDetailsDirective);
    function setupDetailsDirective() {
        return {
            restrict: 'E',
            scope: {
                setup: '='
            },
            templateUrl: 'application/directives/partials/setup-details.html'
        };
    }
}(angular.module('materialscommons')));
<|endoftext|>


<commit_before>import Vue from "vue/dist/vue.esm";
import VueRouter from 'vue-router';
Vue.use(VueRouter);
Vue.config.productionTip = process.env.NODE_ENV == "development";

import store from "../store/index";
import "../config/axios";
import "../directives/selectionchange";

import contenteditableDirective from "vue-contenteditable-directive";
Vue.use(contenteditableDirective);

import TheResource from "../components/TheResource";

document.addEventListener("DOMContentLoaded", () => {
  const routes = [
    { path: '/casebooks/:id/resources/:resource_id/', component: TheResourceBody}
  ];

  const router = new VueRouter({
    routes
  });

  const app = new Vue({
    el: "#app",
    store,
    router,
    components: {
      TheResource
    }
  });

  window.app = app;
});
<commit_msg>Update component name after rebase<commit_after>import Vue from "vue/dist/vue.esm";
import VueRouter from 'vue-router';
Vue.use(VueRouter);
Vue.config.productionTip = process.env.NODE_ENV == "development";

import store from "../store/index";
import "../config/axios";
import "../directives/selectionchange";

import contenteditableDirective from "vue-contenteditable-directive";
Vue.use(contenteditableDirective);

import TheResource from "../components/TheResource";

document.addEventListener("DOMContentLoaded", () => {
  const routes = [
    { path: '/casebooks/:id/resources/:resource_id/', component: TheResource }
  ];

  const router = new VueRouter({
    routes
  });

  const app = new Vue({
    el: "#app",
    store,
    router,
    components: {
      TheResource
    }
  });

  window.app = app;
});
<|endoftext|>


<commit_before>/*

jQuery.Freefocus 0.3.1

Copyright (c) 2013-2014 Ilia Ablamonov. Licensed under the MIT license.

*/

(function($) {
  'use strict';

  /*

  ### $.freefocus({options...}, {moveOptions...})

  Set up keyboard navigation.

  Options:

  - `focusablesSelector` - selector for keyboard navigation targets. default: a long selector describing all focusable options in web browsers.
    You may want to provide something shorter to improve performance or use `:focusable` from jQuery UI.
  - `focusedSelector` - selector for currently focused (or active) element. default: `':focus'`
  - `hoverFocus` - focus target elements on mouse enter. default: `false`
  - `throttle` - throttle key input for specified time (in milliseconds). Uses underscore.js. default: `false`

  Move options are passed to `$.fn.freefocus`

  Usage:
  ```
  $.freefocus({hoverFocus: true})
  ```

  ### $.freefocus('remove')

  Remove previously set keyboard navigation.

  */

  $.freefocus = function(setupOptions, moveOptions) {
    if (setupOptions === 'remove') {
      removeHandlers();
      return;
    }

    setupOptions = $.extend({}, $.freefocus.setupOptions, setupOptions);

    var keyHandler = function(move) {
      var options = $.extend({}, moveOptions, {
        move: move,
        targets: $(setupOptions.focusablesSelector)
      });

      $(setupOptions.focusedSelector).freefocus(options);
    };

    if (setupOptions.throttle !== false)
      keyHandler = _.throttle(keyHandler, setupOptions.throttle);

    addHandler('keydown', function(event) {
      var move = $.freefocus.keys[event.which];

      if (!move)
        return;

      event.preventDefault();

      keyHandler(move);
    });

    if (setupOptions.hoverFocus) {
      addHandler('mouseenter', setupOptions.focusablesSelector, function() {
        var trigger = (moveOptions || {}).trigger || $.freefocus.moveOptions.trigger;
        return $(this).trigger(trigger);
      });
    }
  };

  /*

  ### .freefocus({moveOptions...})

  Move "focus" from active element to one of the targets by triggering specified event.

  Options:

  - `move` - move direction: `left` | `right` | `up` | `down`. no default
  - `targets` - jQuery object containing "focusable" elements. no default
  - `debug` - print weighting information over targets. default: `false`
  - `trigger` - event to trigger on selected target. default: `'focus'`
  - `useNavProps` - respect `nav-*` directional focus navigation style properties. default: `true`
  - `maxDistance` - maximum distance to element to still consider moving to it. default: `Infinity`

  Usage:
  ```
  $(':focus').freefocus({move: 'right', targets: $('[tabindex]')})
  ```

  */

  $.fn.freefocus = function(options) {
    options = $.extend({}, $.freefocus.moveOptions, options);

    if ($.freefocus.moves[options.move] === null)
      throw new Error("Unknown move direction '" + options.move + "'");
    if (!(options.targets instanceof $))
      throw new Error("Argument targets should be a jQuery object");
    if (this.size() > 1)
      throw new Error("Can't move from multiple elements");
    if (!this.size())
      return this; // It's useful to be silent here

    if (options.debug) {
      clearDots();
    }

    updateFocusPoint(this, options.move);

    var to = targetFromNavProps(this, options) || targetWithMinDistance(this, options);

    if (!to)
      return this; // It's useful to be silent here

    moveFocusPoint(to, options.move, options.debug);

    to.trigger(options.trigger);

    return this;
  };

  /*

  Defaults:

  */

  $.freefocus.moveOptions = {
    trigger: 'focus',
    weightFn: $.freefocus.weightFn,
    debug: false,
    useNavProps: true,
    maxDistance: Infinity
  };

  $.freefocus.setupOptions = {
    focusablesSelector: [
      'a[href]',
      'area[href]',
      'input:not([disabled])',
      'select:not([disabled])',
      'textarea:not([disabled])',
      'button:not([disabled])',
      'iframe',
      'object',
      'embed',
      '*[tabindex]',
      '*[contenteditable]'
    ].join(', '),
    focusedSelector: ':focus',
    hoverFocus: false,
    throttle: false
  };

  $.freefocus.keys = {
    37: 'left',
    38: 'up',
    39: 'right',
    40: 'down'
  };

  $.freefocus.moves = {
    left: {},
    right: {},
    up: {},
    down: {}
  };

  $.freefocus.focusPoint = {};

  /*

  Private:

  */

  function targetFromNavProps($el, options) {
    var to =
      $el.get(0).style["nav" + (options.move.charAt(0).toUpperCase()) + (options.move.slice(1))] ||
      parseStyleString($el.attr('style') || '')["nav-" + options.move];

    if (!to)
      return;

    if (to.indexOf('#') !== 0)
      throw new Error("Invalid nav-" + options.move + " selector '" + to + "': only #id allowed.");

    if (!$(to).length)
      return;

    return $(to);
  }

  function parseStyleString(style) {
    var result = {};
    style.split(';').forEach(function (rule) {
      var kv = rule.split(':');
      if (kv[1])
        result[kv[0].trim()] = kv[1].trim();
    });
    return result;
  }

  function updateFocusPoint($el, direction) {
    var box = elementBox($el);
    var fp = $.freefocus.focusPoint;

    // If the element was focused by freefocus,
    // thus we have valid current focus point
    if (!$el.is(fp.$el)) {
      fp.$el = $el;
      fp.box = {
        x: (box.x2 - box.x1) / 2,
        y: (box.y2 - box.y1) / 2
      };
    }
    fp.updatedInDirection = coordsInDirection(boxCoordsToClient(fp.box, box), direction);
    fp.updatedInDirection.fwd = boxInDirection(box, direction).fwd2;
  }

  function moveFocusPoint($el, direction, debug) {
    var box = elementBox($el);
    var fp = $.freefocus.focusPoint;

    fp.$el = $el;

    var boxInDir = boxInDirection(box, direction);
    var movedInDirection = {
      fwd: boxInDir.fwd1,
      ort: bound(fp.updatedInDirection.ort, boxInDir.ort1, boxInDir.ort2)
    };
    fp.box = clientCoordsToBox(coordsFromDirection(movedInDirection, direction), box);

    if (debug) {
      putDot(coordsFromDirection(movedInDirection, direction), '#00f', 'entry focus point for ' + $el.html());
    }
  }

  function targetWithMinDistance($fromEl, options) {
    var fromBox = boxInDirection(elementBox($fromEl), options.move);

    if (options.debug) {
      putDot(coordsFromDirection($.freefocus.focusPoint.updatedInDirection, options.move), '#0f0', 'exit focus point for ' + $fromEl.html());
    }

    var minDistance = Infinity;
    var $resultEl = null;

    options.targets.each(function() {
      var $toEl = $(this);

      // Skip currently focused element
      if ($toEl.is($fromEl))
        return;

      var toBox = boxInDirection(elementBox($toEl), options.move);

      // Skip elements that are not in the direction of movement
      if (toBox.fwd2 <= fromBox.fwd2)
        return;

      var dist = distance(fromBox, toBox);

      if (dist < options.maxDistance && dist < minDistance) {
        $resultEl = $toEl;
        minDistance = dist;
      }
    });

    if (options.debug && $resultEl) {
      console.log("Distance from", $fromEl.get(0), "to", $resultEl.get(0), "is", minDistance);
    }

    return $resultEl;
  }

  function distance(fromBox, toBox) {
    var fromPoint = $.freefocus.focusPoint.updatedInDirection;

    var toPoint = {
      fwd: toBox.fwd1,
      ort: bound(fromPoint.ort, toBox.ort1, toBox.ort2)
    };

    var fwdDist = Math.abs(toPoint.fwd - fromPoint.fwd);
    var ortDist = Math.abs(toPoint.ort - fromPoint.ort);

    // The Euclidian distance between the current focus point position and
    // its potential position in the candidate.
    // If the two positions have the same coordinate on the axis orthogonal
    // to the navigation direction, dotDist is forced to 0 in order to favor
    // elements in direction of navigation
    var dotDist;
    if (toPoint.ort === fromPoint.ort) {
      dotDist = 0;
    } else {
      dotDist = Math.sqrt(fwdDist * fwdDist + ortDist * ortDist);
    }

    // The overlap between the opposing edges of currently focused element and the candidate.
    // Elements are rewarded for having high overlap with the currently focused element.
    var overlap = boxOverlap(fromBox, toBox);

    return dotDist + fwdDist + 2 * ortDist - Math.sqrt(overlap);
  }

  function boxOverlap(box1, box2) {
    var orts = {
      ort1: box1.ort1,
      ort2: box1.ort2
    };

    if (box2.ort1 > orts.ort1)
      orts.ort1 = box2.ort1;
    if (box2.ort2 < orts.ort2)
      orts.ort2 = box2.ort2;

    var result = orts.ort2 - orts.ort1;
    if (result < 0)
      result = 0;

    return result;
  }

  function elementBox($el) {
    if (!$el.box) {
      var offset = $el.offset();
      $el.box = {
        x1: offset.left,
        y1: offset.top,
        x2: offset.left + $el.outerWidth(),
        y2: offset.top + $el.outerHeight()
      };
    }
    return $el.box;
  }

  function boxInDirection(box, direction) {
    var p1 = coordsInDirection({ x: box.x1, y: box.y1 }, direction);
    var p2 = coordsInDirection({ x: box.x2, y: box.y2 }, direction);
    return {
      fwd1: Math.min(p1.fwd, p2.fwd),
      ort1: Math.min(p1.ort, p2.ort),
      fwd2: Math.max(p1.fwd, p2.fwd),
      ort2: Math.max(p1.ort, p2.ort)
    };
  }

  function coordsInDirection(coords, direction) {
    switch (direction) {
      case 'left':
        return { fwd: -coords.x, ort: -coords.y };
      case 'right':
        return { fwd:  coords.x, ort:  coords.y };
      case 'down':
        return { fwd:  coords.y, ort: -coords.x };
      case 'up':
        return { fwd: -coords.y, ort:  coords.x };
    }
  }

  function coordsFromDirection(coords, direction) {
    switch (direction) {
      case 'left':
        return { x: -coords.fwd, y: -coords.ort };
      case 'right':
        return { x:  coords.fwd, y:  coords.ort };
      case 'down':
        return { x: -coords.ort, y:  coords.fwd };
      case 'up':
        return { x:  coords.ort, y: -coords.fwd };
    }
  }

  function boxCoordsToClient(coords, box) {
    return {
      x: coords.x + box.x1,
      y: coords.y + box.y1
    };
  }

  function clientCoordsToBox(coords, box) {
    return {
      x: coords.x - box.x1,
      y: coords.y - box.y1
    };
  }

  function bound(val, min, max) {
    return Math.min(Math.max(val, min), max);
  }

  /*

  Debug:

  */

  function clearDots() {
    $('.freefocus-debug').remove();
  }

  function putDot(coords, color, title) {
    $('

').appendTo($('body')).css({
      position: 'absolute',
      left: coords.x - 3,
      top: coords.y - 3,
      width: 6,
      height: 6,
      background: color,
      'border-radius': 3
    }).attr('title', title);
  }

  /*

  Event handlers:

  */

  var eventHandlers = [];
  var $document = $(document);

  function addHandler() {
    $document.on.apply($document, arguments);
    eventHandlers.push(arguments);
  }

  function removeHandlers() {
    eventHandlers.forEach(function (args) {
      $document.off.apply($document, args);
    });
    eventHandlers = [];
  }
})(jQuery);
<commit_msg>Added caching of dimension data.<commit_after>/*

jQuery.Freefocus 0.3.1

Copyright (c) 2013-2014 Ilia Ablamonov. Licensed under the MIT license.

*/

(function($) {
  'use strict';

  /*

  ### $.freefocus({options...}, {moveOptions...})

  Set up keyboard navigation.

  Options:

  - `focusablesSelector` - selector for keyboard navigation targets. default: a long selector describing all focusable options in web browsers.
    You may want to provide something shorter to improve performance or use `:focusable` from jQuery UI.
  - `focusedSelector` - selector for currently focused (or active) element. default: `':focus'`
  - `hoverFocus` - focus target elements on mouse enter. default: `false`
  - `throttle` - throttle key input for specified time (in milliseconds).
    You'll need underscore.js included to use this feature. default: `false`

  Move options are passed to `$.fn.freefocus`

  Usage:
  ```
  $.freefocus({hoverFocus: true})
  ```

  ### $.freefocus('remove')

  Remove previously set keyboard navigation.

  ### $.freefocus('cache'[, elements])

  Compute and cache dimension information for set of elements. `elements` default: installed `focusablesSelector`

  */

  $.freefocus = function(setupOptions, moveOptions) {
    if (setupOptions === 'remove') {
      removeHandlers();
      return;
    }

    if (setupOptions === 'cache') {
      cacheFocusables(moveOptions);
      return;
    }

    setupOptions = $.extend({}, $.freefocus.setupOptions, setupOptions);

    $.freefocus.installedFocusablesSelector = setupOptions.focusablesSelector;

    var keyHandler = function(move) {
      var options = $.extend({}, moveOptions, {
        move: move,
        targets: $(setupOptions.focusablesSelector).filter(':visible')
      });

      $(setupOptions.focusedSelector).freefocus(options);
    };

    if (setupOptions.throttle !== false)
      keyHandler = _.throttle(keyHandler, setupOptions.throttle);

    addHandler('keydown', function(event) {
      var move = $.freefocus.keys[event.which];

      if (!move)
        return;

      event.preventDefault();

      keyHandler(move);
    });

    if (setupOptions.hoverFocus) {
      addHandler('mouseenter', setupOptions.focusablesSelector, function() {
        var trigger = (moveOptions || {}).trigger || $.freefocus.moveOptions.trigger;
        return $(this).trigger(trigger);
      });
    }
  };

  /*

  ### .freefocus({moveOptions...})

  Move "focus" from active element to one of the targets by triggering specified event.

  Options:

  - `move` - move direction: `left` | `right` | `up` | `down`. no default
  - `targets` - jQuery object containing "focusable" elements. no default
  - `debug` - print weighting information over targets. default: `false`
  - `trigger` - event to trigger on selected target. default: `'focus'`
  - `useNavProps` - respect `nav-*` directional focus navigation style properties. default: `true`
  - `maxDistance` - maximum distance to element to still consider moving to it. default: `Infinity`
  - `cache` - cache dimension information for element. default: `false`
    You'll need to manually reset cache for moved elements by using `$(element).freefocus('moved')`

  Usage:
  ```
  $(':focus').freefocus({move: 'right', targets: $('[tabindex]')})
  ```

  ### .freefocus('dimensions')

  Get dimension object for element. Uses cache, if it's enabled.

  ### .freefocus('moved')

  Clears cache for element.

  */

  $.fn.freefocus = function(options) {
    if (options === 'dimensions') {
      var box = getElementBox(this, true);
      return {
        left: box.x1,
        top: box.y1,
        width: box.x2 - box.x1,
        height: box.y2 - box.y1
      };
    }

    if (options === 'moved') {
      this.data('freefocus-dimensions', null);
      return;
    }

    options = $.extend({}, $.freefocus.moveOptions, options);

    if ($.freefocus.moves[options.move] === null)
      throw new Error("Unknown move direction '" + options.move + "'");
    if (!(options.targets instanceof $))
      throw new Error("Argument targets should be a jQuery object");
    if (this.size() > 1)
      throw new Error("Can't move from multiple elements");
    if (!this.size())
      return this; // It's useful to be silent here

    if (options.debug) {
      clearDots();
    }

    updateFocusPoint(this, options.move, options.cache);

    var to = targetFromNavProps(this, options) || targetWithMinDistance(this, options);

    if (!to)
      return this; // It's useful to be silent here

    moveFocusPoint(to, options.move, options.debug, options.cache);

    to.trigger(options.trigger);

    return this;
  };

  /*

  Defaults:

  */

  $.freefocus.moveOptions = {
    trigger: 'focus',
    weightFn: $.freefocus.weightFn,
    debug: false,
    useNavProps: true,
    maxDistance: Infinity
  };

  $.freefocus.setupOptions = {
    focusablesSelector: [
      'a[href]',
      'area[href]',
      'input:not([disabled])',
      'select:not([disabled])',
      'textarea:not([disabled])',
      'button:not([disabled])',
      'iframe',
      'object',
      'embed',
      '*[tabindex]',
      '*[contenteditable]'
    ].join(', '),
    focusedSelector: ':focus',
    hoverFocus: false,
    throttle: false
  };

  $.freefocus.keys = {
    37: 'left',
    38: 'up',
    39: 'right',
    40: 'down'
  };

  $.freefocus.moves = {
    left: {},
    right: {},
    up: {},
    down: {}
  };

  /*

  Private:

  */

  $.freefocus.focusPoint = {};
  $.freefocus.installedFocusablesSelector = null;

  function targetFromNavProps($el, options) {
    var to =
      $el.get(0).style["nav" + (options.move.charAt(0).toUpperCase()) + (options.move.slice(1))] ||
      parseStyleString($el.attr('style') || '')["nav-" + options.move];

    if (!to)
      return;

    if (to.indexOf('#') !== 0)
      throw new Error("Invalid nav-" + options.move + " selector '" + to + "': only #id allowed.");

    if (!$(to).length)
      return;

    return $(to);
  }

  function parseStyleString(style) {
    var result = {};
    style.split(';').forEach(function (rule) {
      var kv = rule.split(':');
      if (kv[1])
        result[kv[0].trim()] = kv[1].trim();
    });
    return result;
  }

  function updateFocusPoint($el, direction, cache) {
    var box = getElementBox($el, cache);
    var fp = $.freefocus.focusPoint;

    // If the element was focused by freefocus,
    // thus we have valid current focus point
    if (!$el.is(fp.$el)) {
      fp.$el = $el;
      fp.box = {
        x: (box.x2 - box.x1) / 2,
        y: (box.y2 - box.y1) / 2
      };
    }
    fp.updatedInDirection = coordsInDirection(boxCoordsToClient(fp.box, box), direction);
    fp.updatedInDirection.fwd = boxInDirection(box, direction).fwd2;
  }

  function moveFocusPoint($el, direction, debug, cache) {
    var box = getElementBox($el, cache);
    var fp = $.freefocus.focusPoint;

    fp.$el = $el;

    var boxInDir = boxInDirection(box, direction);
    var movedInDirection = {
      fwd: boxInDir.fwd1,
      ort: bound(fp.updatedInDirection.ort, boxInDir.ort1, boxInDir.ort2)
    };
    fp.box = clientCoordsToBox(coordsFromDirection(movedInDirection, direction), box);

    if (debug) {
      putDot(coordsFromDirection(movedInDirection, direction), '#00f', 'entry focus point for ' + $el.html());
    }
  }

  function targetWithMinDistance($fromEl, options) {
    var fromBox = boxInDirection(getElementBox($fromEl, options.cache), options.move);

    if (options.debug) {
      putDot(coordsFromDirection($.freefocus.focusPoint.updatedInDirection, options.move), '#0f0', 'exit focus point for ' + $fromEl.html());
    }

    var minDistance = Infinity;
    var $resultEl = null;

    options.targets.each(function() {
      var $toEl = $(this);

      // Skip currently focused element
      if ($toEl.is($fromEl))
        return;

      var toBox = boxInDirection(getElementBox($toEl, options.cache), options.move);

      // Skip elements that are not in the direction of movement
      if (toBox.fwd2 <= fromBox.fwd2)
        return;

      var dist = distance(fromBox, toBox);

      if (dist < options.maxDistance && dist < minDistance) {
        $resultEl = $toEl;
        minDistance = dist;
      }
    });

    if (options.debug && $resultEl) {
      console.log("Distance from", $fromEl.get(0), "to", $resultEl.get(0), "is", minDistance);
    }

    return $resultEl;
  }

  function distance(fromBox, toBox) {
    var fromPoint = $.freefocus.focusPoint.updatedInDirection;

    var toPoint = {
      fwd: toBox.fwd1,
      ort: bound(fromPoint.ort, toBox.ort1, toBox.ort2)
    };

    var fwdDist = Math.abs(toPoint.fwd - fromPoint.fwd);
    var ortDist = Math.abs(toPoint.ort - fromPoint.ort);

    // The Euclidian distance between the current focus point position and
    // its potential position in the candidate.
    // If the two positions have the same coordinate on the axis orthogonal
    // to the navigation direction, dotDist is forced to 0 in order to favor
    // elements in direction of navigation
    var dotDist;
    if (toPoint.ort === fromPoint.ort) {
      dotDist = 0;
    } else {
      dotDist = Math.sqrt(fwdDist * fwdDist + ortDist * ortDist);
    }

    // The overlap between the opposing edges of currently focused element and the candidate.
    // Elements are rewarded for having high overlap with the currently focused element.
    var overlap = boxOverlap(fromBox, toBox);

    return dotDist + fwdDist + 2 * ortDist - Math.sqrt(overlap);
  }

  function boxOverlap(box1, box2) {
    var orts = {
      ort1: box1.ort1,
      ort2: box1.ort2
    };

    if (box2.ort1 > orts.ort1)
      orts.ort1 = box2.ort1;
    if (box2.ort2 < orts.ort2)
      orts.ort2 = box2.ort2;

    var result = orts.ort2 - orts.ort1;
    if (result < 0)
      result = 0;

    return result;
  }

  function computeElementBox($el) {
    var offset = $el.offset();
    return {
      x1: offset.left,
      y1: offset.top,
      x2: offset.left + $el.outerWidth(),
      y2: offset.top + $el.outerHeight()
    };
  }

  function getElementBox($el, cache) {
    if (!cache)
      return computeElementBox($el);

    var box = $el.data('freefocus-dimensions');
    if (!box) {
      box = computeElementBox($el);
      $el.data('freefocus-dimensions', box);
    }
    return box;
  }

  function boxInDirection(box, direction) {
    var p1 = coordsInDirection({ x: box.x1, y: box.y1 }, direction);
    var p2 = coordsInDirection({ x: box.x2, y: box.y2 }, direction);
    return {
      fwd1: Math.min(p1.fwd, p2.fwd),
      ort1: Math.min(p1.ort, p2.ort),
      fwd2: Math.max(p1.fwd, p2.fwd),
      ort2: Math.max(p1.ort, p2.ort)
    };
  }

  function coordsInDirection(coords, direction) {
    switch (direction) {
      case 'left':
        return { fwd: -coords.x, ort: -coords.y };
      case 'right':
        return { fwd:  coords.x, ort:  coords.y };
      case 'down':
        return { fwd:  coords.y, ort: -coords.x };
      case 'up':
        return { fwd: -coords.y, ort:  coords.x };
    }
  }

  function coordsFromDirection(coords, direction) {
    switch (direction) {
      case 'left':
        return { x: -coords.fwd, y: -coords.ort };
      case 'right':
        return { x:  coords.fwd, y:  coords.ort };
      case 'down':
        return { x: -coords.ort, y:  coords.fwd };
      case 'up':
        return { x:  coords.ort, y: -coords.fwd };
    }
  }

  function boxCoordsToClient(coords, box) {
    return {
      x: coords.x + box.x1,
      y: coords.y + box.y1
    };
  }

  function clientCoordsToBox(coords, box) {
    return {
      x: coords.x - box.x1,
      y: coords.y - box.y1
    };
  }

  function bound(val, min, max) {
    return Math.min(Math.max(val, min), max);
  }

  function cacheFocusables(selector) {
    if (!selector) {
      selector = $.freefocus.installedFocusablesSelector;
    }
    $(selector).filter(':visible').each(function () {
      getElementBox($(this), true);
    });
  }

  /*

  Debug:

  */

  function clearDots() {
    $('.freefocus-debug').remove();
  }

  function putDot(coords, color, title) {
    $('

').appendTo($('body')).css({
      position: 'absolute',
      left: coords.x - 3,
      top: coords.y - 3,
      width: 6,
      height: 6,
      background: color,
      'border-radius': 3
    }).attr('title', title);
  }

  /*

  Event handlers:

  */

  var eventHandlers = [];
  var $document = $(document);

  function addHandler() {
    $document.on.apply($document, arguments);
    eventHandlers.push(arguments);
  }

  function removeHandlers() {
    eventHandlers.forEach(function (args) {
      $document.off.apply($document, args);
    });
    eventHandlers = [];
  }
})(jQuery);
<|endoftext|>


<commit_before>'use strict'

var path = require('path')
var thermostat = require('thermostat')
var traverse = require('traverse')
var WinkClient = require('../lib/wink')

/**
 * Compiles any "$EXPRESSION" template strings in `obj`, returning a function
 * that renders those strings from a single parameter, `curves`. The parameter
 * should contain the current state of all curves, represented as an Object.
 */
function compile(obj) {
  obj = traverse(obj).map(function (value) {
    if (this.isLeaf && String(value)[0] === '$') {
      this.update(new Function('curves', 'return ' + String(value).substr(1)))
    }
  })

  return function render(curves) {
    return traverse(obj).map(function (value) {
      if (typeof value === 'function') {
        this.update(value(curves))
      }
    })
  }
}

/**
 * Fulfills the `start` command. See README and `man/start` for more
 * information.
 */
function start(argv, options, loader) {
  var manifest = require(path.resolve(process.cwd(), argv[0]))
  var curves = Object.keys(manifest.curves)
    .map(function (key) {
      var curve = thermostat.createCurve(manifest.curves[key])
      curve.key = key
      return curve
    })
  var devices = Object.keys(manifest.devices)
    .map(function (key) {
      var compiled = compile(manifest.devices[key])
      compiled.key = key
      return compiled
    })
  var curveData = {}
  var deviceState = {}

  return WinkClient
    .login(manifest.account)
    .then(function (client) {
      return (function tick() {
        var now = Date.now()

        curves.forEach(function (curve) {
          curveData[curve.key] = curve(now)
        })

        devices.forEach(function (device) {
          deviceState[device.key] = device(curveData)
        })

        return client.setDeviceState(deviceState)
          .catch(function (err) {
            process.stderr.write('Error during setDeviceState: %s', err.message || err)
          })
          .delay(5000)
          .then(tick)
      }())
    })
}

/*!
 * Export `start`.
 */
module.exports = start
<commit_msg>Start: Only submit changes.<commit_after>'use strict'

var path = require('path')
var util = require('util')
var Promise = require('bluebird')
var thermostat = require('thermostat')
var traverse = require('traverse')
var WinkClient = require('../lib/wink')

/**
 * Compiles any "$EXPRESSION" template strings in `obj`, returning a function
 * that renders those strings from a single parameter, `curves`. The parameter
 * should contain the current state of all curves, represented as an Object.
 */
function compile(obj) {
  obj = traverse(obj).map(function (value) {
    if (this.isLeaf && String(value)[0] === '$') {
      this.update(new Function('curves', 'return ' + String(value).substr(1)))
    }
  })

  return function render(curves) {
    return traverse(obj).map(function (value) {
      if (typeof value === 'function') {
        this.update(value(curves))
      }
    })
  }
}

/**
 * Returns a hash of `obj` suitable for change detection.
 */
function hash(obj) {
  return JSON.stringify(obj)
}

/**
 * Fulfills the `start` command. See README and `man/start` for more
 * information.
 */
function start(argv, options, loader) {
  var manifest = require(path.resolve(process.cwd(), argv[0]))
  var curves = Object.keys(manifest.curves)
    .map(function (key) {
      var curve = thermostat.createCurve(manifest.curves[key])
      curve.key = key
      return curve
    })
  var devices = Object.keys(manifest.devices)
    .map(function (key) {
      var compiled = compile(manifest.devices[key])
      compiled.key = key
      return compiled
    })
  var curveData = {}
  var deviceState = {}
  var deviceStateHash = {}

  return WinkClient
    .login(manifest.account)
    .then(function (client) {
      process.stdout.write('Starting...\n')

      return (function tick() {
        var now = Date.now()
        var changes = {}
        var promise

        curves.forEach(function (curve) {
          curveData[curve.key] = curve(now)
        })

        devices.forEach(function (device) {
          var newState = device(curveData)
          var newHash = hash(newState)

          if (newHash !== deviceStateHash[device.key]) {
            deviceState[device.key] = newState
            deviceStateHash[device.key] = newHash
            changes[device.key] = newState
          }
        })

        if (Object.keys(changes).length === 0) {
          promise = Promise.resolve()
        } else {
          process.stdout.write(util.format('Setting device state to: %j\n', changes))
          promise = client.setDeviceState(changes)
            .catch(function (err) {
              process.stderr.write(util.format('Error during setDeviceState: %s\n', err.message || err))
            })
            .then(function () {
              process.stdout.write('Accepted.\n')
            })
        }

        return promise
          .delay(5000)
          .then(tick)
      }())
    })
}

/*!
 * Export `start`.
 */
module.exports = start
<|endoftext|>