<commit_before>import Ember from 'ember';
import onlineJob from 'ember-data-offline/jobs/online';
import offlineJob from 'ember-data-offline/jobs/localstorage';
// import DS from 'ember-data';
const { Mixin, $, on, assert, computed, get, isPresent } = Ember;

export default Mixin.create({
  isOffline: computed.not('isOnline'),
  onlineJob: onlineJob,
  offlineJob: offlineJob,

  store: computed({
    get() {
      return this.container.lookup('store:main');
    }
  }),
  _workingQueue: computed('queue', {
    get() {
      if (isPresent(get(this, 'queue'))) {
        return get(this, 'queue');
      } else {
        return get(this, 'store.queue');
      }
    }
  }),

  addToQueue(job){
    console.log(job);
  },

  createOnlineJob(method, params){
    let job = this.get('onlineJob').create({
      adapter: this,
      method: method,
      params: params
    });
    this.addToQueue(job);
  },

  createOfflineJob(method, params){
    let job = this.get('offlineJob').create({
      adapter: this.get('offlineAdapter'),
      method: method,
      params: params
    });
    this.addToQueue(job);
  },

  /*
   * `createRecord()`
   * `updateRecord()`
   * `deleteRecord()`
   */

  findAll: function(store, typeClass, sinceToken) {
    if (this.get('isOffline')) {
      this.createOnlineJob('findAll', [store, typeClass, sinceToken]);
      return this.get('offlineAdapter').findAll(store, typeClass, sinceToken);
    }

    let adapterResp = this._super.apply(this, arguments);
    this.createOfflineJob('findAll', [store, typeClass, sinceToken, adapterResp]);
    return adapterResp;
  },

  find: function(store, typeClass, id, snapshot) {
    if (this.get('isOffline')) {
      return this.get('offlineAdapter').find(store, typeClass, id, snapshot);
    }
    let onlineResp = this._super.apply(this, arguments);
    this.createOfflineJob('find', [store, typeClass, id, snapshot, onlineResp]);
    return onlineResp;
  },

  findQuery: function(store, type, query) {
    if (this.get('isOffline')) {
      return this.get('offlineAdapter').findQuery(store, type, query);
    }
    let onlineResp = this._super.apply(this, arguments);
    this.createOfflineJob('findQuery', [store, type, query, onlineResp]);
    return onlineResp;
  },

  findMany: function(store, type, ids, snapshots) {
    if (this.get('isOffline')) {
      return this.get('offlineAdapter').find(store, type, ids, snapshots);
    }
    let onlineResp = this._super.apply(this, arguments);
    this.createOfflineJob('find', [store, type, ids, snapshots, onlineResp]);
    return onlineResp;
  },

  createRecord(store, type, snapshot) {
    //TODO check difference between online/offline there
    if (this.get('isOffline')) {
      this.createOnlineJob('createRecord', [store, type, snapshot]);
      return this.get('offlineAdapter').createRecord(store, type, snapshot);
    }
    this.createOnlineJob('createRecord', [store, type, snapshot]);
    return this.get('offlineAdapter').createRecord(store, type, snapshot);
  },

  updateRecord(store, type, snapshot) {
    //TODO check difference between online/offline there
    if (this.get('isOffline')) {
      this.createOnlineJob('updateRecord', [store, type, snapshot]);
      return this.get('offlineAdapter').updateRecord(store, type, snapshot);
    }
    this.createOnlineJob('updateRecord', [store, type, snapshot]);
    return this.get('offlineAdapter').updateRecord(store, type, snapshot);
  },

  deleteRecord(store, type, snapshot) {
    //TODO check difference between online/offline there
    if (this.get('isOffline')) {
      this.createOnlineJob('deleteRecord', [store, type, snapshot]);
      return this.get('offlineAdapter').deleteRecord(store, type, snapshot);
    }
    this.createOnlineJob('deleteRecord', [store, type, snapshot]);
    return this.get('offlineAdapter').deleteRecord(store, type, snapshot);
  },

  assertRunner: on('init', function() {
    assert('[ember-data-offline] You should set offline adapter', get(this, 'offlineAdapter'));
  }),

  setup: on('init', function() {
    $(window).on('online', () => {
      this.set('isOnline', true);
    });
    $(window).on('offline', () => {
      this.set('isOnline', false);
    });
    this.set('isOnline', window.navigator.onLine);
  }),

  teardown: on('willDestroy', function() {
    $(window).off('online');
    $(window).off('offline');
  }),
});
<commit_msg>#8 small fix<commit_after>import Ember from 'ember';
import onlineJob from 'ember-data-offline/jobs/online';
import offlineJob from 'ember-data-offline/jobs/localstorage';
// import DS from 'ember-data';
const { Mixin, $, on, assert, computed, get, isPresent } = Ember;

export default Mixin.create({
  isOffline: computed.not('isOnline'),
  onlineJob: onlineJob,
  offlineJob: offlineJob,

  store: computed({
    get() {
      return this.container.lookup('store:main');
    }
  }),
  _workingQueue: computed('queue', {
    get() {
      if (isPresent(get(this, 'queue'))) {
        return get(this, 'queue');
      } else {
        return get(this, 'store.queue');
      }
    }
  }),

  addToQueue(job){
    this.get('_workingQueue').add(job);
  },

  createOnlineJob(method, params){
    let job = this.get('onlineJob').create({
      adapter: this,
      method: method,
      params: params
    });
    this.addToQueue(job);
  },

  createOfflineJob(method, params){
    let job = this.get('offlineJob').create({
      adapter: this.get('offlineAdapter'),
      method: method,
      params: params
    });
    this.addToQueue(job);
  },

  /*
   * `createRecord()`
   * `updateRecord()`
   * `deleteRecord()`
   */

  findAll: function(store, typeClass, sinceToken) {
    if (this.get('isOffline')) {
      this.createOnlineJob('findAll', [store, typeClass, sinceToken]);
      return this.get('offlineAdapter').findAll(store, typeClass, sinceToken);
    }

    let adapterResp = this._super.apply(this, arguments);
    this.createOfflineJob('findAll', [store, typeClass, sinceToken, adapterResp]);
    return adapterResp;
  },

  find: function(store, typeClass, id, snapshot) {
    if (this.get('isOffline')) {
      return this.get('offlineAdapter').find(store, typeClass, id, snapshot);
    }
    let onlineResp = this._super.apply(this, arguments);
    this.createOfflineJob('find', [store, typeClass, id, snapshot, onlineResp]);
    return onlineResp;
  },

  findQuery: function(store, type, query) {
    if (this.get('isOffline')) {
      return this.get('offlineAdapter').findQuery(store, type, query);
    }
    let onlineResp = this._super.apply(this, arguments);
    this.createOfflineJob('findQuery', [store, type, query, onlineResp]);
    return onlineResp;
  },

  findMany: function(store, type, ids, snapshots) {
    if (this.get('isOffline')) {
      return this.get('offlineAdapter').find(store, type, ids, snapshots);
    }
    let onlineResp = this._super.apply(this, arguments);
    this.createOfflineJob('find', [store, type, ids, snapshots, onlineResp]);
    return onlineResp;
  },

  createRecord(store, type, snapshot) {
    //TODO check difference between online/offline there
    if (this.get('isOffline')) {
      this.createOnlineJob('createRecord', [store, type, snapshot]);
      return this.get('offlineAdapter').createRecord(store, type, snapshot);
    }
    this.createOnlineJob('createRecord', [store, type, snapshot]);
    return this.get('offlineAdapter').createRecord(store, type, snapshot);
  },

  updateRecord(store, type, snapshot) {
    //TODO check difference between online/offline there
    if (this.get('isOffline')) {
      this.createOnlineJob('updateRecord', [store, type, snapshot]);
      return this.get('offlineAdapter').updateRecord(store, type, snapshot);
    }
    this.createOnlineJob('updateRecord', [store, type, snapshot]);
    return this.get('offlineAdapter').updateRecord(store, type, snapshot);
  },

  deleteRecord(store, type, snapshot) {
    //TODO check difference between online/offline there
    if (this.get('isOffline')) {
      this.createOnlineJob('deleteRecord', [store, type, snapshot]);
      return this.get('offlineAdapter').deleteRecord(store, type, snapshot);
    }
    this.createOnlineJob('deleteRecord', [store, type, snapshot]);
    return this.get('offlineAdapter').deleteRecord(store, type, snapshot);
  },

  assertRunner: on('init', function() {
    assert('[ember-data-offline] You should set offline adapter', get(this, 'offlineAdapter'));
  }),

  setup: on('init', function() {
    $(window).on('online', () => {
      this.set('isOnline', true);
    });
    $(window).on('offline', () => {
      this.set('isOnline', false);
    });
    this.set('isOnline', window.navigator.onLine);
  }),

  teardown: on('willDestroy', function() {
    $(window).off('online');
    $(window).off('offline');
  }),
});
<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  get,
  set,
  isArray,
  run
} = Ember;

export default Ember.Object.extend(Ember.Evented, {
  /**
   * Target url to upload to
   *
   * @property url
   */
  url: null,

  /**
   * ajax request method, by default it will be POST
   *
   * @property method
   */
  method: 'POST',

  /**
   * Used to define a namespace for the file param and any extra data params
   * that may be sent
   *
   * @property paramNamespace
   */
  paramNamespace: null,

  /**
   * The parameter name for the file(s) to be uploaded
   *
   * @property paramName
   */
  paramName: 'file',

  /**
   * Boolean property changed to true upon upload start and false upon upload
   * end
   *
   * @property isUploading
   */
  isUploading: false,

  /**
   * The ajax settings used in all requests made from the uploader
   *
   * @property ajaxSettings
   */
  ajaxSettings: {},

  /**
   * Start upload of file(s) and any extra data
   *
   * @param  {object|array} files  One file object or one array of files object
   * @param  {object} extra Extra data to be sent with the upload
   * @return {object} Returns a Ember.RSVP.Promise wrapping the ajax request
   * object
   */
  upload (files, extra = {}) {
    const data   = this.createFormData(files, extra);
    const url    = get(this, 'url');
    const method = get(this, 'method');

    set(this, 'isUploading', true);

    return this.ajax(url, data, method);
  },

  /**
   * Creates the FormData object with the file(s) and any extra data
   *
   * @param {object|array} files One file object or an array of file objects
   * @param {object} extra Extra data to be sent with the upload
   * @return {object} Returns a FormData object with the supplied file(s) and
   * extra data
   */
  createFormData (files, extra = {}) {
    const formData = new FormData();

    for (const prop in extra) {
      if (extra.hasOwnProperty(prop)) {
        formData.append(this.toNamespacedParam(prop), extra[prop]);
      }
    }

    // if is a array of files ...
    if (isArray(files)) {
      let paramName;

      for (let i = files.length - 1; i >= 0; i--) {
        paramName = `${this.toNamespacedParam(this.paramName)}[][${i}]`;
        formData.append(paramName, files[i]);
      }
    } else {
      // if has only one file object ...
      formData.append(this.toNamespacedParam(this.paramName), files);
    }

    return formData;
  },

  /**
   * Returns the param name namespaced if a namespace exists
   *
   * @param {string} name The param name to namespace
   * @return {string} Returns the namespaced param
   */
  toNamespacedParam (name) {
    return this.paramNamespace ?
      `${this.paramNamespace}[${name}]` :
      name;
  },

  /**
   * Triggers didUpload event with given params and sets isUploading to false
   *
   * @param {object} data Object of data supplied to the didUpload event
   * @return {object} Returns the given data
   */
  didUpload (data) {
    set(this, 'isUploading', false);
    this.trigger('didUpload', data);
    return data;
  },

  /**
   * Triggers didError event with given params and sets isUploading to false
   *
   * @param {object} jqXHR jQuery XMLHttpRequest object
   * @param {string} textStatus The status code of the error
   * @param {object} errorThrown The error caused
   * @return {object} Returns the jQuery XMLHttpRequest
   */
  didError (jqXHR, textStatus, errorThrown) {
    set(this, 'isUploading', false);

    // Borrowed from Ember Data
    const isObject = jqXHR !== null && typeof jqXHR === 'object';

    if (isObject) {
      jqXHR.then = null;
      if (!jqXHR.errorThrown) {
        if (typeof errorThrown === 'string') {
          jqXHR.errorThrown = new Error(errorThrown);
        } else {
          jqXHR.errorThrown = errorThrown;
        }
      }
    }

    this.trigger('didError', jqXHR, textStatus, errorThrown);

    return jqXHR;
  },

  /**
   * Triggers progress event supplying event with current percent
   *
   * @param {object} event Event from xhr onprogress
   */
  didProgress (event) {
    event.percent = event.loaded / event.total * 100;
    this.trigger('progress', event);
  },

  /**
   * Triggers isAborting event and sets isUploading to false
   */
  abort () {
    set(this, 'isUploading', false);
    this.trigger('isAborting');
  },

  /**
   * Starts a request to the given url sending the supplied data using the
   * supplied request method
   *
   * @param {string} url The target url for the request
   * @param {object} data The data to send with the request
   * @param {string} method The request method
   * @return {object} Returns a Ember.RSVP.Promise wrapping the ajax request
   * object
   */
  ajax (url, data = {}, method = this.method) {
    return this.ajaxPromise({
      ...this.ajaxSettings,
      contentType: false,
      processData: false,
      xhr: () => {
        const xhr = Ember.$.ajaxSettings.xhr();
        xhr.upload.onprogress = (event) => {
          this.didProgress(event);
        };
        this.one('isAborting', () => xhr.abort());
        return xhr;
      },
      url,
      data,
      method
    });
  },

  /**
   * Starts a request using the supplied settings returning a
   * Ember.RSVP.Promise wrapping the ajax request
   *
   * @param {object} settings The jQuery.ajax compatible settings object
   * @return {object} Returns a Ember.RSVP.Promise wrapping the ajax request
   */
  ajaxPromise (settings) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      settings.success = (data) => {
        run(null, resolve, this.didUpload(data));
      };

      settings.error = (jqXHR, responseText, errorThrown) => {
        run(null, reject, this.didError(jqXHR, responseText, errorThrown));
      };

      Ember.$.ajax(settings);
    });
  }
});
<commit_msg>Fix multiple file upload<commit_after>import Ember from 'ember';

const {
  get,
  set,
  isArray,
  run
} = Ember;

export default Ember.Object.extend(Ember.Evented, {
  /**
   * Target url to upload to
   *
   * @property url
   */
  url: null,

  /**
   * ajax request method, by default it will be POST
   *
   * @property method
   */
  method: 'POST',

  /**
   * Used to define a namespace for the file param and any extra data params
   * that may be sent
   *
   * @property paramNamespace
   */
  paramNamespace: null,

  /**
   * The parameter name for the file(s) to be uploaded
   *
   * @property paramName
   */
  paramName: 'file',

  /**
   * Boolean property changed to true upon upload start and false upon upload
   * end
   *
   * @property isUploading
   */
  isUploading: false,

  /**
   * The ajax settings used in all requests made from the uploader
   *
   * @property ajaxSettings
   */
  ajaxSettings: {},

  /**
   * Start upload of file(s) and any extra data
   *
   * @param  {object|array} files  One file object or one array of files object
   * @param  {object} extra Extra data to be sent with the upload
   * @return {object} Returns a Ember.RSVP.Promise wrapping the ajax request
   * object
   */
  upload (files, extra = {}) {
    const data   = this.createFormData(files, extra);
    const url    = get(this, 'url');
    const method = get(this, 'method');

    set(this, 'isUploading', true);

    return this.ajax(url, data, method);
  },

  /**
   * Creates the FormData object with the file(s) and any extra data
   *
   * @param {object|array} files One file object or an array of file objects
   * @param {object} extra Extra data to be sent with the upload
   * @return {object} Returns a FormData object with the supplied file(s) and
   * extra data
   */
  createFormData (files, extra = {}) {
    const formData = new FormData();

    for (const prop in extra) {
      if (extra.hasOwnProperty(prop)) {
        formData.append(this.toNamespacedParam(prop), extra[prop]);
      }
    }

    // if is a array of files ...
    if (isArray(files)) {
      for (let i = 0; i < files.length; i++) {
        formData.append(`${this.toNamespacedParam(this.paramName)}[]`, files[i]);
      }
    } else {
      // if has only one file object ...
      formData.append(this.toNamespacedParam(this.paramName), files);
    }

    return formData;
  },

  /**
   * Returns the param name namespaced if a namespace exists
   *
   * @param {string} name The param name to namespace
   * @return {string} Returns the namespaced param
   */
  toNamespacedParam (name) {
    return this.paramNamespace ?
      `${this.paramNamespace}[${name}]` :
      name;
  },

  /**
   * Triggers didUpload event with given params and sets isUploading to false
   *
   * @param {object} data Object of data supplied to the didUpload event
   * @return {object} Returns the given data
   */
  didUpload (data) {
    set(this, 'isUploading', false);
    this.trigger('didUpload', data);
    return data;
  },

  /**
   * Triggers didError event with given params and sets isUploading to false
   *
   * @param {object} jqXHR jQuery XMLHttpRequest object
   * @param {string} textStatus The status code of the error
   * @param {object} errorThrown The error caused
   * @return {object} Returns the jQuery XMLHttpRequest
   */
  didError (jqXHR, textStatus, errorThrown) {
    set(this, 'isUploading', false);

    // Borrowed from Ember Data
    const isObject = jqXHR !== null && typeof jqXHR === 'object';

    if (isObject) {
      jqXHR.then = null;
      if (!jqXHR.errorThrown) {
        if (typeof errorThrown === 'string') {
          jqXHR.errorThrown = new Error(errorThrown);
        } else {
          jqXHR.errorThrown = errorThrown;
        }
      }
    }

    this.trigger('didError', jqXHR, textStatus, errorThrown);

    return jqXHR;
  },

  /**
   * Triggers progress event supplying event with current percent
   *
   * @param {object} event Event from xhr onprogress
   */
  didProgress (event) {
    event.percent = event.loaded / event.total * 100;
    this.trigger('progress', event);
  },

  /**
   * Triggers isAborting event and sets isUploading to false
   */
  abort () {
    set(this, 'isUploading', false);
    this.trigger('isAborting');
  },

  /**
   * Starts a request to the given url sending the supplied data using the
   * supplied request method
   *
   * @param {string} url The target url for the request
   * @param {object} data The data to send with the request
   * @param {string} method The request method
   * @return {object} Returns a Ember.RSVP.Promise wrapping the ajax request
   * object
   */
  ajax (url, data = {}, method = this.method) {
    return this.ajaxPromise({
      ...this.ajaxSettings,
      contentType: false,
      processData: false,
      xhr: () => {
        const xhr = Ember.$.ajaxSettings.xhr();
        xhr.upload.onprogress = (event) => {
          this.didProgress(event);
        };
        this.one('isAborting', () => xhr.abort());
        return xhr;
      },
      url,
      data,
      method
    });
  },

  /**
   * Starts a request using the supplied settings returning a
   * Ember.RSVP.Promise wrapping the ajax request
   *
   * @param {object} settings The jQuery.ajax compatible settings object
   * @return {object} Returns a Ember.RSVP.Promise wrapping the ajax request
   */
  ajaxPromise (settings) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      settings.success = (data) => {
        run(null, resolve, this.didUpload(data));
      };

      settings.error = (jqXHR, responseText, errorThrown) => {
        run(null, reject, this.didError(jqXHR, responseText, errorThrown));
      };

      Ember.$.ajax(settings);
    });
  }
});
<|endoftext|>


<commit_before>define(["views/dialog"], function(GitDialog) {
    var commands = codebox.require("core/commands/toolbar");
    var app = codebox.require("core/app");
    var box = codebox.require("core/box");
    var dialogs = codebox.require("utils/dialogs");
    var menu = codebox.require("core/commands/menu");
    var box = codebox.require("core/box");
    var Command = codebox.require("models/command");
    var rpc = codebox.require("core/backends/rpc");
    var operations = codebox.require("core/operations");

    // Check git status
    var updateStatus = function() {
        return rpc.execute("git/status")
        .then(function() {
            updateMenu(true);
            return updateBranchesMenu();
        }, function(err) {
            updateMenu(false);
        })
    };

    // Branches menu
    var branchesMenu = Command.register({
        'title': "Switch To Branch",
        'type': "menu",
        'offline': false
    });
    var updateBranchesMenu = function() {
        return rpc.execute("git/branches")
        .then(function(branches) {
            branchesMenu.menu.reset(_.map(branches, function(branch) {
                return {
                    'title': branch.name,
                    'flags': branch.active ? "active" : "",
                    'action': function() {
                        var ref = branch.name;
                        return operations.start("git.checkout", function(op) {
                            return rpc.execute("git/checkout", {
                                'ref': ref
                            })
                        }, {
                            title: "Checkout '"+ref+"'"
                        }).then(updateBranchesMenu);
                    }
                }
            }));
        });
    };

    // Add menu
    var gitMenu = menu.register("git", {
        title: "Repository"
    });

    var updateMenu = function(state) {
        // Clear menu
        gitMenu.clearMenu();

        // Invalid repository
        if (!state) {
            gitMenu.menuSection([
                {
                    'title': "Invalid GIT repository",
                    'type': "label",
                    'iconMenu': "warning"
                },
                {
                    'title': "Initialize Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.init", function(op) {
                            return rpc.execute("git/init")
                        }, {
                            title: "Initializing GIT repository"
                        });
                    }
                },
                {
                    'title': "Clone Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.clone", function(op) {
                            return rpc.execute("git/clone")
                        }, {
                            title: "Cloning GIT repository"
                        });
                    }
                }
            ]);
        } else {
            gitMenu.menuSection([
                {
                    'title': "Synchronize",
                    'shortcuts': ["mod+S"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.sync", function(op) {
                            return rpc.execute("git/sync")
                        }, {
                            title: "Pushing & Pulling"
                        });
                    }
                }
            ]).menuSection([
                {
                    'title': "Commit",
                    'shortcuts': ["mod+shift+C"],
                    'offline': false,
                    'action': function() {
                        dialogs.open(GitDialog);
                    }
                }
            ]).menuSection([
                {
                    'title': "Push",
                    'shortcuts': ["mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.push", function(op) {
                            return rpc.execute("git/push")
                        }, {
                            title: "Pushing"
                        });
                    }
                },
                {
                    'title': "Pull",
                    'shortcuts': ["shift+mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.pull", function(op) {
                            return rpc.execute("git/pull")
                        }, {
                            title: "Pulling"
                        });
                    }
                }
            ]).menuSection([
                branchesMenu,
                {
                    'title': "Refresh branches",
                    'offline': false,
                    'action': updateBranchesMenu,
                }
            ]).menuSection([
                {
                    'title': "Create a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Create a branch", "Enter the name for the new branch:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.create", function(op) {
                                return rpc.execute("git/branch/create", {
                                    'name': name
                                })
                            }, {
                                title: "Creating branch '"+name+"'"
                            });
                        });
                    }
                },
                {
                    'title': "Delete a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Delete a branch", "Enter the name of the branch you want to delete:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.delete", function(op) {
                                return rpc.execute("git/branch/delete", {
                                    'name': name
                                })
                            }, {
                                title: "Deleting branch '"+name+"'"
                            }).then(updateBranchesMenu);
                        });
                    }
                }
            ]);
        }
    };

    box.on("box:git", function() {
        updateStatus();
    });
    updateStatus();
});

<commit_msg>Change position of command "synchronize"<commit_after>define(["views/dialog"], function(GitDialog) {
    var commands = codebox.require("core/commands/toolbar");
    var app = codebox.require("core/app");
    var box = codebox.require("core/box");
    var dialogs = codebox.require("utils/dialogs");
    var menu = codebox.require("core/commands/menu");
    var box = codebox.require("core/box");
    var Command = codebox.require("models/command");
    var rpc = codebox.require("core/backends/rpc");
    var operations = codebox.require("core/operations");

    // Check git status
    var updateStatus = function() {
        return rpc.execute("git/status")
        .then(function() {
            updateMenu(true);
            return updateBranchesMenu();
        }, function(err) {
            updateMenu(false);
        })
    };

    // Branches menu
    var branchesMenu = Command.register({
        'title': "Switch To Branch",
        'type': "menu",
        'offline': false
    });
    var updateBranchesMenu = function() {
        return rpc.execute("git/branches")
        .then(function(branches) {
            branchesMenu.menu.reset(_.map(branches, function(branch) {
                return {
                    'title': branch.name,
                    'flags': branch.active ? "active" : "",
                    'action': function() {
                        var ref = branch.name;
                        return operations.start("git.checkout", function(op) {
                            return rpc.execute("git/checkout", {
                                'ref': ref
                            })
                        }, {
                            title: "Checkout '"+ref+"'"
                        }).then(updateBranchesMenu);
                    }
                }
            }));
        });
    };

    // Add menu
    var gitMenu = menu.register("git", {
        title: "Repository"
    });

    var updateMenu = function(state) {
        // Clear menu
        gitMenu.clearMenu();

        // Invalid repository
        if (!state) {
            gitMenu.menuSection([
                {
                    'title': "Invalid GIT repository",
                    'type': "label",
                    'iconMenu': "warning"
                },
                {
                    'title': "Initialize Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.init", function(op) {
                            return rpc.execute("git/init")
                        }, {
                            title: "Initializing GIT repository"
                        });
                    }
                },
                {
                    'title': "Clone Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.clone", function(op) {
                            return rpc.execute("git/clone")
                        }, {
                            title: "Cloning GIT repository"
                        });
                    }
                }
            ]);
        } else {
            gitMenu.menuSection([
                {
                    'title': "Commit",
                    'shortcuts': ["mod+shift+C"],
                    'offline': false,
                    'action': function() {
                        dialogs.open(GitDialog);
                    }
                }
            ]).menuSection([
                {
                    'title': "Synchronize",
                    'shortcuts': ["mod+S"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.sync", function(op) {
                            return rpc.execute("git/sync")
                        }, {
                            title: "Pushing & Pulling"
                        });
                    }
                },
                {
                    'title': "Push",
                    'shortcuts': ["mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.push", function(op) {
                            return rpc.execute("git/push")
                        }, {
                            title: "Pushing"
                        });
                    }
                },
                {
                    'title': "Pull",
                    'shortcuts': ["shift+mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.pull", function(op) {
                            return rpc.execute("git/pull")
                        }, {
                            title: "Pulling"
                        });
                    }
                }
            ]).menuSection([
                branchesMenu,
                {
                    'title': "Refresh branches",
                    'offline': false,
                    'action': updateBranchesMenu,
                }
            ]).menuSection([
                {
                    'title': "Create a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Create a branch", "Enter the name for the new branch:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.create", function(op) {
                                return rpc.execute("git/branch/create", {
                                    'name': name
                                })
                            }, {
                                title: "Creating branch '"+name+"'"
                            });
                        });
                    }
                },
                {
                    'title': "Delete a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Delete a branch", "Enter the name of the branch you want to delete:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.delete", function(op) {
                                return rpc.execute("git/branch/delete", {
                                    'name': name
                                })
                            }, {
                                title: "Deleting branch '"+name+"'"
                            }).then(updateBranchesMenu);
                        });
                    }
                }
            ]);
        }
    };

    box.on("box:git", function() {
        updateStatus();
    });
    updateStatus();
});

<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

// Application bootstrapper
module.exports = Em.Application.create({
  //LOG_TRANSITIONS: true,
  //LOG_TRANSITIONS_INTERNAL: true
  smokeTests: false,

  errorLog: "",

  getNamespaceUrl: function() {
    var parts = window.location.pathname.match(/\/[^\/]*/g);
    var view = parts[1];
    var version = '/versions' + parts[2];
    var instance = parts[3];
    if (parts.length == 4) { // version is not present
      instance = parts[2];
      version = '';
    }
    var namespaceUrl = 'api/v1/views' + view + version + '/instances' + instance;
    return namespaceUrl;
  },

  Resolver: Ember.DefaultResolver.extend({
    resolveTemplate: function(parsedName) {
      var resolvedTemplate = this._super(parsedName);
      var templateName = 'templates/' + parsedName.fullNameWithoutType.replace(/\./g, '/');
      if (resolvedTemplate) {
        return resolvedTemplate;
      } else {
        return Ember.TEMPLATES[templateName];
      }
    }
  })
});
<commit_msg>AMBARI-20641. Pig view fails to load when using ambari thru KNOX. (gauravn7)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

// Application bootstrapper
module.exports = Em.Application.create({
  //LOG_TRANSITIONS: true,
  //LOG_TRANSITIONS_INTERNAL: true
  smokeTests: false,

  errorLog: "",

  getNamespaceUrl: function() {
    var parts = window.location.pathname.split('/').filter(function (i) {
      return i !== "";
    });
    var view = parts[parts.length - 3];
    var version = '/versions/' + parts[parts.length - 2];
    var instance = parts[parts.length - 1];

    if (!/^(\d+\.){2,3}\d+$/.test(parts[parts.length - 2])) { // version is not present
      instance = parts[parts.length - 2];
      version = '';
    }
    var namespaceUrl = 'api/v1/views/' + view + version + '/instances/' + instance;
    return namespaceUrl.replace(/^\/|\/$/g, ''); //remove starting slash if proxied through knox
  },

  Resolver: Ember.DefaultResolver.extend({
    resolveTemplate: function(parsedName) {
      var resolvedTemplate = this._super(parsedName);
      var templateName = 'templates/' + parsedName.fullNameWithoutType.replace(/\./g, '/');
      if (resolvedTemplate) {
        return resolvedTemplate;
      } else {
        return Ember.TEMPLATES[templateName];
      }
    }
  })
});
<|endoftext|>


<commit_before>// function definitions section
lx.lionixCRM.getEnvironment = function() {
    if ($("#LionixCRM-environment").length == 0) {
        if (lx.lionixCRM.config.environment.toLowerCase() === 'testing') {
            let pbclass = "progress-bar bg-info";
            let probability = 100;
            let text_bar = "Testing Environment"
            $('#content').before('

' + text_bar + '

');
        } else {
            $('#content').before('
');
        }
        console.log("LionixCRM-environment added.");
    } else {
        console.log("LionixCRM-environment exists.");
    }
} // end function

lx.lionixCRM.checkEnvironment = function() {
    if (lx.lionixCRM.config.environment == undefined) {
        lx.lionixCRM.getConfigOption('environment').then(function() {
            lx.lionixCRM.getEnvironment()
        });
    } else {
        lx.lionixCRM.getEnvironment();
    }
} // end function
//eof

// This file containts all modules bussines logic
//Self-Invoking Anonymous Function Notation
// !function(){}(); // easy to read, the result is unimportant.
// (function(){})(); // like above but more parens.
// (function(){}()); // Douglas Crockford's style when you need function results.
// Further reading: http://javascript.crockford.com/code.html then search for invoked immediately
!function() {
    // create an observer instance
    // https://developer.mozilla.org/en/docs/Web/API/MutationObserver
    var observer = new MutationObserver(function(mutations) {
        if (mutations) {
            if ($("#LionixCRM-environment").length == 0) {
                console.groupCollapsed("Check environment '%s' '%s' '%s' '%s'", 'all modules', 'lx-check-crm-environment.js', '!function()', 'observer');
                console.log('Running lx.lionixCRM.checkEnvironment() function');
                console.groupEnd();
                lx.lionixCRM.checkEnvironment();
            }
            // if needed only once, you can stop observing with observer.disconnect();
            observer.disconnect();
        }
        // When need to find something special
        // mutations.forEach(function(mutation) {
        //     console.log('select2-lionixcrm mutation:', mutation);
        //     //     if (mutation.type == "attributes") {
        //     //         if (mutation.target.nodeName == "FORM" && mutation.target.id == "some-form-id") {
        //     //             //when found do your code
        //     //             //your code
        //     //             // if needed only once, you can stop observing with observer.disconnect();
        //     //             //observer.disconnect();
        //     //         }
        //     //     }
        // });
    });
    // Observer target
    var target = document.querySelector('#content');
    // configuration of the observer:
    // NOTE: At the very least, childList, attributes, or characterData must be set to true. Otherwise, "An invalid or illegal string was specified" error is thrown.
    var config = {
        attributes: true,
        childList: true,
        // characterData: true,
        subtree: true
    };
    // pass in the target node, as well as the observer options
    observer.observe(target, config);
    // end observer
}();
<commit_msg>refactor: fewer functions needed<commit_after>// function definitions section
lx.lionixCRM.getEnvironment = function() {
    lx.lionixCRM.getConfigOption('environment').then(function(data) {
        console.log("environment:", data);
        if ($("#LionixCRM-environment").length == 0) {
            if (lx.lionixCRM.config.environment.toLowerCase() === 'testing') {
                let pbclass = "progress-bar bg-info";
                let probability = 100;
                let text_bar = "Testing Environment"
                $('#content').before('

' + text_bar + '

');
            } else {
                $('#content').before('
');
            }
            console.log("LionixCRM-environment added.");
        } else {
            console.log("LionixCRM-environment exists.");
        }
    });
} // end function

// This file containts all modules bussines logic
//Self-Invoking Anonymous Function Notation
// !function(){}(); // easy to read, the result is unimportant.
// (function(){})(); // like above but more parens.
// (function(){}()); // Douglas Crockford's style when you need function results.
// Further reading: http://javascript.crockford.com/code.html then search for invoked immediately
!function() {
    // create an observer instance
    // https://developer.mozilla.org/en/docs/Web/API/MutationObserver
    var observer = new MutationObserver(function(mutations) {
        if (mutations) {
            console.log("Check environment '%s' '%s' '%s' '%s'", 'all modules', 'lx-check-crm-environment.js', '!function()', 'observer');
            console.log('Running lx.lionixCRM.checkEnvironment() function');
            lx.lionixCRM.getEnvironment();
            // if needed only once, you can stop observing with observer.disconnect();
            observer.disconnect();
        }
    });
    // Observer target
    var target = document.querySelector('#content');
    // configuration of the observer:
    // NOTE: At the very least, childList, attributes, or characterData must be set to true. Otherwise, "An invalid or illegal string was specified" error is thrown.
    var config = {
        attributes: true,
        childList: true,
        // characterData: true,
        subtree: true
    };
    // pass in the target node, as well as the observer options
    observer.observe(target, config);
    // end observer
}();
<|endoftext|>


<commit_before>describe('Legacy redirects', () => {
  it('should redirect from front-end-libraries to front-end-development-libraries', () => {
    cy.visit('learn/front-end-libraries');
    cy.url().should('include', 'learn/front-end-development-libraries');

    cy.visit('learn/front-end-libraries/bootstrap');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/bootstrap/'
      );
    });

    cy.visit('learn/front-end-libraries/front-end-libraries-projects');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/front-end-development-libraries-projects/'
      );
    });

    cy.visit(
      'learn/front-end-libraries/front-end-libraries-projects/build-a-random-quote-machine'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/front-end-development-libraries-projects/build-a-random-quote-machine'
      );
    });

    cy.visit('certification/certifieduser/front-end-libraries');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/certification/certifieduser/front-end-development-libraries'
      );
    });
  });

  it('should load this one challenge that throws an error if we do not test it separately', () => {
    cy.visit(
      'learn/front-end-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers'
      );
    });
  });

  it('should redirect from /apis-and-microservices to /back-end-development-and-apis', () => {
    cy.visit('learn/apis-and-microservices');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq('/learn/back-end-development-and-apis/');
    });

    cy.visit('learn/apis-and-microservices/managing-packages-with-npm');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/managing-packages-with-npm/'
      );
    });

    cy.visit(
      'learn/apis-and-microservices/managing-packages-with-npm/how-to-use-package-json-the-core-of-any-node-js-project-or-npm-package'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/managing-packages-with-npm/how-to-use-package-json-the-core-of-any-node-js-project-or-npm-package'
      );
    });

    cy.visit('learn/apis-and-microservices/apis-and-microservices-projects');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/back-end-development-and-apis-projects/'
      );
    });

    cy.visit(
      'learn/apis-and-microservices/apis-and-microservices-projects/timestamp-microservice'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/back-end-development-and-apis-projects/timestamp-microservice'
      );
    });

    cy.visit('certification/certifieduser/apis-and-microservices');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/certification/certifieduser/back-end-development-and-apis'
      );
    });
  });
});
<commit_msg>test: fix flaky redirect tests (#43243)<commit_after>// These tests require the client to be built and served with additional
// redirect configuration. The Cypress action in .github/workflows/cypress.yml
// contains the necessary commands to do this.

describe('Legacy redirects', () => {
  it('should redirect from front-end-libraries to front-end-development-libraries', () => {
    cy.visit('learn/front-end-libraries');
    cy.url().should('include', 'learn/front-end-development-libraries');

    cy.visit('learn/front-end-libraries/bootstrap');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/bootstrap/'
      );
    });

    cy.visit('learn/front-end-libraries/front-end-libraries-projects');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/front-end-development-libraries-projects/'
      );
    });

    cy.visit(
      'learn/front-end-libraries/front-end-libraries-projects/build-a-random-quote-machine'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/front-end-development-libraries-projects/build-a-random-quote-machine'
      );
    });

    cy.visit('certification/certifieduser/front-end-libraries');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/certification/certifieduser/front-end-development-libraries'
      );
    });

    cy.visit(
      'learn/front-end-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/front-end-development-libraries/bootstrap/use-responsive-design-with-bootstrap-fluid-containers'
      );
    });
    // Bit of hack: but we need to make sure the page is fully loaded before
    // moving on.
    cy.get('.react-monaco-editor-container').should('be.visible');
  });

  it('should redirect from /apis-and-microservices to /back-end-development-and-apis', () => {
    cy.visit('learn/apis-and-microservices');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq('/learn/back-end-development-and-apis/');
    });

    cy.visit('learn/apis-and-microservices/managing-packages-with-npm');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/managing-packages-with-npm/'
      );
    });

    cy.visit(
      'learn/apis-and-microservices/managing-packages-with-npm/how-to-use-package-json-the-core-of-any-node-js-project-or-npm-package'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/managing-packages-with-npm/how-to-use-package-json-the-core-of-any-node-js-project-or-npm-package'
      );
    });

    cy.visit('learn/apis-and-microservices/apis-and-microservices-projects');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/back-end-development-and-apis-projects/'
      );
    });

    cy.visit(
      'learn/apis-and-microservices/apis-and-microservices-projects/timestamp-microservice'
    );
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/learn/back-end-development-and-apis/back-end-development-and-apis-projects/timestamp-microservice'
      );
    });

    cy.visit('certification/certifieduser/apis-and-microservices');
    cy.location().should(loc => {
      expect(loc.pathname).to.eq(
        '/certification/certifieduser/back-end-development-and-apis'
      );
    });
  });
});
<|endoftext|>


<commit_before>const pool = require('../pool')
const {poolQuery} = require('../helpers')
const {requireAuth} = require('../auth')
const {processedString, processedTitleString} = require('../helpers/stringHelpers')
const {fetchPlaylists} = require('../helpers/playlistHelpers')
const async = require('async')
const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  const playlistId = typeof req.query.playlistId !== 'undefined' ? Number(req.query.playlistId) : null
  const where = playlistId !== null ? 'WHERE a.id < ' + playlistId + ' ' : ''
  const query = [
    'SELECT a.id, a.title, a.creator AS uploaderId, b.username AS uploader ',
    'FROM vq_playlists a LEFT JOIN users b ON a.creator = b.id ',
    where,
    'ORDER BY a.id DESC LIMIT 4'
  ].join('')
  fetchPlaylists(query, (err, playlists) => {
    if (err) {
      console.error(err)
      res.status(500).send({error: err})
      return
    }
    res.send({playlists})
  })
})

router.post('/', requireAuth, (req, res) => {
  const user = req.user
  const rawDescription = (!req.body.description || req.body.description === '') ?
    'No description' : req.body.description
  const title = processedTitleString(req.body.title)
  const description = processedString(rawDescription)
  const videos = req.body.selectedVideos
  const taskArray = []
  async.waterfall([
    (callback) => {
      const uploaderId = user.id
      const uploaderName = user.username
      const post = {title, description, creator: uploaderId}
      pool.query('INSERT INTO vq_playlists SET ?', post, (err, res) => {
        const playlistId = res.insertId
        callback(err, playlistId, uploaderName, uploaderId)
      })
    },
    (playlistId, uploaderName, uploaderId, callback) => {
      for (let i = 0; i < videos.length; i++) {
        taskArray.push(callback => {
          let playlistVideo = {playlistId: playlistId, videoId: videos[i]}
          pool.query('INSERT INTO vq_playlistvideos SET ?', playlistVideo, function(err) {
            callback(err)
          })
        })
      }
      async.series(taskArray, function(err) {
        if (err) return callback(err)
        const query = [
          'SELECT a.id, a.videoId, b.title AS video_title, b.videoCode, c.username AS video_uploader, ',
          'COUNT(d.id) AS numLikes ',
          'FROM vq_playlistvideos a JOIN vq_videos b ON a.videoId = b.id LEFT JOIN users c ON b.uploader = c.id ',
          'LEFT JOIN vq_video_likes d ON b.id = d.videoId ',
          'WHERE a.playlistId = ? GROUP BY a.videoId ORDER BY a.id'
        ].join('')
        pool.query(query, playlistId, (err, rows) => {
          callback(err, {
            playlist: rows,
            title: title,
            id: playlistId,
            uploader: uploaderName,
            uploaderId: uploaderId
          })
        })
      })
    }
  ], (err, result) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({result})
  })
})

router.post('/edit/title', requireAuth, (req, res) => {
  const title = req.body.title
  const playlistId = req.body.playlistId
  const newTitle = processedTitleString(title)
  const post = {
    title: newTitle
  }
  pool.query('UPDATE vq_playlists SET ? WHERE id = ?', [post, playlistId], err => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.json({result: newTitle})
  })
})

router.post('/edit/videos', requireAuth, (req, res) => {
  const playlistId = req.body.playlistId
  const selectedVideos = req.body.selectedVideos

  return poolQuery('DELETE FROM vq_playlistvideos WHERE playlistId = ?', playlistId).then(
    () => {
      let tasks = []
      for (let i = 0; i < selectedVideos.length; i++) {
        tasks.push(() => poolQuery('INSERT INTO vq_playlistvideos SET ?', {playlistId, videoId: selectedVideos[i]}))
      }
      return tasks.reduce((promise, task) => promise.then(task), Promise.resolve())
    }
  ).then(
    () => {
      let query = `
        SELECT a.id, a.videoId, b.title AS video_title, b.videoCode, c.username AS video_uploader,
        COUNT(d.id) AS numLikes
        FROM vq_playlistvideos a
          JOIN vq_videos b ON a.videoId = b.id
          LEFT JOIN users c ON b.uploader = c.id
          LEFT JOIN vq_video_likes d ON b.id = d.videoId
        WHERE a.playlistId = ? GROUP BY a.videoId ORDER BY a.id
      `
      return poolQuery(query, playlistId)
    }
  ).then(
    result => res.send({result})
  ).catch(
    err => {
      console.error(err)
      res.status(500).send({error: err})
    }
  )
})

router.delete('/', requireAuth, (req, res) => {
  const playlistId = typeof req.query.playlistId !== 'undefined' ? Number(req.query.playlistId) : 0
  async.waterfall([
    (callback) => {
      pool.query('SELECT * FROM vq_playlists WHERE id = ?', playlistId, (err, rows) => {
        if (!rows || rows.length === 0) return callback('User is not the owner of the playlist')
        callback(err)
      })
    },
    (callback) => {
      async.parallel([
        (callback) => {
          pool.query('DELETE FROM vq_playlists WHERE id = ?', playlistId, (err, result) => {
            callback(err, result)
          })
        },
        (callback) => {
          pool.query('DELETE FROM vq_pinned_playlists WHERE playlistId = ?', playlistId, (err, result) => {
            callback(err, result)
          })
        },
        (callback) => {
          pool.query('DELETE FROM vq_playlistvideos WHERE playlistId = ?', playlistId, (err, result) => {
            callback(err, result)
          })
        }
      ], (err, results) => {
        callback(err, results)
      })
    }
  ], (err, result) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({success: true})
  })
})

router.get('/pinned', (req, res) => {
  const query = [
    'SELECT a.id, a.title, a.creator AS uploaderId, b.username AS uploader ',
    'FROM vq_playlists a JOIN vq_pinned_playlists c ON c.playlistId = a.id ',
    'LEFT JOIN users b ON a.creator = b.id ORDER BY c.id DESC'
  ].join('')
  fetchPlaylists(query, (err, playlists) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({playlists})
  })
})

router.post('/pinned', requireAuth, (req, res) => {
  const user = req.user
  const selectedPlaylists = req.body.selectedPlaylists

  if (selectedPlaylists.length > 5) {
    return res.status(500).send({error: 'Maximum playlist number exceeded'})
  }
  async.waterfall([
    (callback) => {
      const userType = user.userType
      if (userType !== 'master') {
        return callback('User is not authorized to perform this action')
      }
      pool.query('SELECT * FROM vq_pinned_playlists', (err, rows) => {
        if (rows) {
          pool.query('TRUNCATE vq_pinned_playlists', err => {
            if (err) {
              return callback(err)
            }
            callback(err)
          })
        } else {
          callback(err)
        }
      })
    },
    callback => {
      if (selectedPlaylists.length === 0) {
        callback(null)
      } else {
        let taskArray = []
        for (let i = selectedPlaylists.length - 1; i >= 0; i--) {
          taskArray.push(callback => {
            pool.query('INSERT INTO vq_pinned_playlists SET ?', {playlistId: selectedPlaylists[i]}, err => {
              callback(err)
            })
          })
        }
        async.series(taskArray, (err) => {
          callback(err)
        })
      }
    },
    callback => {
      const query = [
        'SELECT a.id, a.title, a.creator AS uploaderId, b.username AS uploader ',
        'FROM vq_playlists a JOIN vq_pinned_playlists c ON c.playlistId = a.id ',
        'LEFT JOIN users b ON a.creator = b.id ORDER BY c.id DESC'
      ].join('')
      fetchPlaylists(query, (err, playlists) => {
        callback(err, playlists)
      })
    }
  ], (err, playlists) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.json({playlists})
  })
})

router.get('/list', (req, res) => {
  const playlistId = req.query.playlistId ? Number(req.query.playlistId) : 0
  const where = playlistId !== 0 ? 'WHERE id < ' + playlistId + ' ' : ''
  const query = [
    'SELECT id, title FROM vq_playlists ',
    where,
    'ORDER BY id DESC LIMIT 11'
  ].join('')
  pool.query(query, (err, rows) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({result: rows})
  })
})

module.exports = router
<commit_msg>improved playlist load query<commit_after>const pool = require('../pool')
const {poolQuery} = require('../helpers')
const {requireAuth} = require('../auth')
const {processedString, processedTitleString} = require('../helpers/stringHelpers')
const {fetchPlaylists} = require('../helpers/playlistHelpers')
const async = require('async')
const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  const playlistId = typeof req.query.playlistId !== 'undefined' ? Number(req.query.playlistId) : null
  const where = playlistId !== null ? 'WHERE a.id < ' + playlistId + ' ' : ''
  const query = [
    'SELECT a.id, a.title, a.creator AS uploaderId, b.username AS uploader ',
    'FROM vq_playlists a LEFT JOIN users b ON a.creator = b.id GROUP BY a.id ',
    where,
    'ORDER BY a.id DESC LIMIT 4'
  ].join('')
  fetchPlaylists(query, (err, playlists) => {
    if (err) {
      console.error(err)
      res.status(500).send({error: err})
      return
    }
    res.send({playlists})
  })
})

router.post('/', requireAuth, (req, res) => {
  const user = req.user
  const rawDescription = (!req.body.description || req.body.description === '') ?
    'No description' : req.body.description
  const title = processedTitleString(req.body.title)
  const description = processedString(rawDescription)
  const videos = req.body.selectedVideos
  const taskArray = []
  async.waterfall([
    (callback) => {
      const uploaderId = user.id
      const uploaderName = user.username
      const post = {title, description, creator: uploaderId}
      pool.query('INSERT INTO vq_playlists SET ?', post, (err, res) => {
        const playlistId = res.insertId
        callback(err, playlistId, uploaderName, uploaderId)
      })
    },
    (playlistId, uploaderName, uploaderId, callback) => {
      for (let i = 0; i < videos.length; i++) {
        taskArray.push(callback => {
          let playlistVideo = {playlistId: playlistId, videoId: videos[i]}
          pool.query('INSERT INTO vq_playlistvideos SET ?', playlistVideo, function(err) {
            callback(err)
          })
        })
      }
      async.series(taskArray, function(err) {
        if (err) return callback(err)
        const query = [
          'SELECT a.id, a.videoId, b.title AS video_title, b.videoCode, c.username AS video_uploader, ',
          'COUNT(d.id) AS numLikes ',
          'FROM vq_playlistvideos a JOIN vq_videos b ON a.videoId = b.id LEFT JOIN users c ON b.uploader = c.id ',
          'LEFT JOIN vq_video_likes d ON b.id = d.videoId ',
          'WHERE a.playlistId = ? GROUP BY a.videoId ORDER BY a.id'
        ].join('')
        pool.query(query, playlistId, (err, rows) => {
          callback(err, {
            playlist: rows,
            title: title,
            id: playlistId,
            uploader: uploaderName,
            uploaderId: uploaderId
          })
        })
      })
    }
  ], (err, result) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({result})
  })
})

router.post('/edit/title', requireAuth, (req, res) => {
  const title = req.body.title
  const playlistId = req.body.playlistId
  const newTitle = processedTitleString(title)
  const post = {
    title: newTitle
  }
  pool.query('UPDATE vq_playlists SET ? WHERE id = ?', [post, playlistId], err => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.json({result: newTitle})
  })
})

router.post('/edit/videos', requireAuth, (req, res) => {
  const playlistId = req.body.playlistId
  const selectedVideos = req.body.selectedVideos

  return poolQuery('DELETE FROM vq_playlistvideos WHERE playlistId = ?', playlistId).then(
    () => {
      let tasks = []
      for (let i = 0; i < selectedVideos.length; i++) {
        tasks.push(() => poolQuery('INSERT INTO vq_playlistvideos SET ?', {playlistId, videoId: selectedVideos[i]}))
      }
      return tasks.reduce((promise, task) => promise.then(task), Promise.resolve())
    }
  ).then(
    () => {
      let query = `
        SELECT a.id, a.videoId, b.title AS video_title, b.videoCode, c.username AS video_uploader,
        COUNT(d.id) AS numLikes
        FROM vq_playlistvideos a
          JOIN vq_videos b ON a.videoId = b.id
          LEFT JOIN users c ON b.uploader = c.id
          LEFT JOIN vq_video_likes d ON b.id = d.videoId
        WHERE a.playlistId = ? GROUP BY a.videoId ORDER BY a.id
      `
      return poolQuery(query, playlistId)
    }
  ).then(
    result => res.send({result})
  ).catch(
    err => {
      console.error(err)
      res.status(500).send({error: err})
    }
  )
})

router.delete('/', requireAuth, (req, res) => {
  const playlistId = typeof req.query.playlistId !== 'undefined' ? Number(req.query.playlistId) : 0
  async.waterfall([
    (callback) => {
      pool.query('SELECT * FROM vq_playlists WHERE id = ?', playlistId, (err, rows) => {
        if (!rows || rows.length === 0) return callback('User is not the owner of the playlist')
        callback(err)
      })
    },
    (callback) => {
      async.parallel([
        (callback) => {
          pool.query('DELETE FROM vq_playlists WHERE id = ?', playlistId, (err, result) => {
            callback(err, result)
          })
        },
        (callback) => {
          pool.query('DELETE FROM vq_pinned_playlists WHERE playlistId = ?', playlistId, (err, result) => {
            callback(err, result)
          })
        },
        (callback) => {
          pool.query('DELETE FROM vq_playlistvideos WHERE playlistId = ?', playlistId, (err, result) => {
            callback(err, result)
          })
        }
      ], (err, results) => {
        callback(err, results)
      })
    }
  ], (err, result) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({success: true})
  })
})

router.get('/pinned', (req, res) => {
  const query = [
    'SELECT a.id, a.title, a.creator AS uploaderId, b.username AS uploader ',
    'FROM vq_playlists a JOIN vq_pinned_playlists c ON c.playlistId = a.id ',
    'LEFT JOIN users b ON a.creator = b.id ORDER BY c.id DESC'
  ].join('')
  fetchPlaylists(query, (err, playlists) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({playlists})
  })
})

router.post('/pinned', requireAuth, (req, res) => {
  const user = req.user
  const selectedPlaylists = req.body.selectedPlaylists

  if (selectedPlaylists.length > 5) {
    return res.status(500).send({error: 'Maximum playlist number exceeded'})
  }
  async.waterfall([
    (callback) => {
      const userType = user.userType
      if (userType !== 'master') {
        return callback('User is not authorized to perform this action')
      }
      pool.query('SELECT * FROM vq_pinned_playlists', (err, rows) => {
        if (rows) {
          pool.query('TRUNCATE vq_pinned_playlists', err => {
            if (err) {
              return callback(err)
            }
            callback(err)
          })
        } else {
          callback(err)
        }
      })
    },
    callback => {
      if (selectedPlaylists.length === 0) {
        callback(null)
      } else {
        let taskArray = []
        for (let i = selectedPlaylists.length - 1; i >= 0; i--) {
          taskArray.push(callback => {
            pool.query('INSERT INTO vq_pinned_playlists SET ?', {playlistId: selectedPlaylists[i]}, err => {
              callback(err)
            })
          })
        }
        async.series(taskArray, (err) => {
          callback(err)
        })
      }
    },
    callback => {
      const query = [
        'SELECT a.id, a.title, a.creator AS uploaderId, b.username AS uploader ',
        'FROM vq_playlists a JOIN vq_pinned_playlists c ON c.playlistId = a.id ',
        'LEFT JOIN users b ON a.creator = b.id ORDER BY c.id DESC'
      ].join('')
      fetchPlaylists(query, (err, playlists) => {
        callback(err, playlists)
      })
    }
  ], (err, playlists) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.json({playlists})
  })
})

router.get('/list', (req, res) => {
  const playlistId = req.query.playlistId ? Number(req.query.playlistId) : 0
  const where = playlistId !== 0 ? 'WHERE id < ' + playlistId + ' ' : ''
  const query = [
    'SELECT id, title FROM vq_playlists ',
    where,
    'ORDER BY id DESC LIMIT 11'
  ].join('')
  pool.query(query, (err, rows) => {
    if (err) {
      console.error(err)
      return res.status(500).send({error: err})
    }
    res.send({result: rows})
  })
})

module.exports = router
<|endoftext|>


<commit_before>/*global define*/
define([
        '../Core/defaultValue',
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/destroyObject',
        '../Core/DeveloperError',
        '../Core/Event'
    ], function(
        defaultValue,
        defined,
        defineProperties,
        destroyObject,
        DeveloperError,
        Event) {
    "use strict";

    /**
     * A collection of {@link DataSource} instances.
     * @alias DataSourceCollection
     * @constructor
     */
    var DataSourceCollection = function() {
        this._dataSources = [];
        this._dataSourceAdded = new Event();
        this._dataSourceRemoved = new Event();
    };

    defineProperties(DataSourceCollection.prototype, {
        /**
         * Gets the number of data sources in this collection.
         * @memberof DataSourceCollection.prototype
         * @type {Event}
         * @readonly
         */
        length : {
            get : function() {
                return this._dataSources.length;
            }
        },

        /**
         * An event that is raised when a data source is added to the collection.
         * Event handlers are passed the data source that was added.
         * @memberof DataSourceCollection.prototype
         * @type {Event}
         * @readonly
         */
        dataSourceAdded : {
            get : function() {
                return this._dataSourceAdded;
            }
        },

        /**
         * An event that is raised when a data source is removed from the collection.
         * Event handlers are passed the data source that was removed.
         * @memberof DataSourceCollection.prototype
         * @type {Event}
         * @readonly
         */
        dataSourceRemoved : {
            get : function() {
                return this._dataSourceRemoved;
            }
        }
    });

    /**
     * Adds a data source to the collection.
     *
     * @param {DataSource} dataSource The data source to add.
     */
    DataSourceCollection.prototype.add = function(dataSource) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(dataSource)) {
            throw new DeveloperError('dataSource is required.');
        }
        //>>includeEnd('debug');

        this._dataSources.push(dataSource);
        this._dataSourceAdded.raiseEvent(this, dataSource);
    };

    /**
     * Removes a data source from this collection, if present.
     *
     * @param {DataSource} dataSource The data source to remove.
     * @param {Boolean} [destroy=false] Whether to destroy the data source in addition to removing it.
     *
     * @returns {Boolean} true if the data source was in the collection and was removed,
     *                    false if the data source was not in the collection.
     */
    DataSourceCollection.prototype.remove = function(dataSource, destroy) {
        destroy = defaultValue(destroy, false);

        var index = this._dataSources.indexOf(dataSource);
        if (index !== -1) {
            this._dataSources.splice(index, 1);
            this._dataSourceRemoved.raiseEvent(this, dataSource);

            if (destroy && typeof dataSource.destroy === 'function') {
                dataSource.destroy();
            }

            return true;
        }

        return false;
    };

    /**
     * Removes all data sources from this collection.
     *
     * @param {Boolean} [destroy=false] whether to destroy the data sources in addition to removing them.
     */
    DataSourceCollection.prototype.removeAll = function(destroy) {
        destroy = defaultValue(destroy, false);

        var dataSources = this._dataSources;
        for (var i = 0, len = dataSources.length; i < len; ++i) {
            var dataSource = dataSources[i];
            this._dataSourceRemoved.raiseEvent(this, dataSource);

            if (destroy && typeof dataSource.destroy === 'function') {
                dataSource.destroy();
            }
        }
    };

    /**
     * Checks to see if the collection contains a given data source.
     *
     * @param {DataSource} dataSource The data source to check for.
     *
     * @returns {Boolean} true if the collection contains the data source, false otherwise.
     */
    DataSourceCollection.prototype.contains = function(dataSource) {
        return this.indexOf(dataSource) !== -1;
    };

    /**
     * Determines the index of a given data source in the collection.
     *
     * @param {DataSource} dataSource The data source to find the index of.
     *
     * @returns {Number} The index of the data source in the collection, or -1 if the data source does not exist in the collection.
     */
    DataSourceCollection.prototype.indexOf = function(dataSource) {
        return this._dataSources.indexOf(dataSource);
    };

    /**
     * Gets a data source by index from the collection.
     *
     * @param {Number} index the index to retrieve.
     */
    DataSourceCollection.prototype.get = function(index) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(index)) {
            throw new DeveloperError('index is required.');
        }
        //>>includeEnd('debug');

        return this._dataSources[index];
    };

    /**
     * Returns true if this object was destroyed; otherwise, false.
     * If this object was destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.
     *
     * @returns {Boolean} true if this object was destroyed; otherwise, false.
     *
     * @see DataSourceCollection#destroy
     */
    DataSourceCollection.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the resources held by all data sources in this collection.  Explicitly destroying this
     * object allows for deterministic release of WebGL resources, instead of relying on the garbage
     * collector. Once this object is destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (undefined) to the object as done in the example.
     *
     * @returns {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see DataSourceCollection#isDestroyed
     *
     * @example
     * dataSourceCollection = dataSourceCollection && dataSourceCollection.destroy();
     */
    DataSourceCollection.prototype.destroy = function() {
        this.removeAll(true);
        return destroyObject(this);
    };

    return DataSourceCollection;
});<commit_msg>Fix type information.<commit_after>/*global define*/
define([
        '../Core/defaultValue',
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/destroyObject',
        '../Core/DeveloperError',
        '../Core/Event'
    ], function(
        defaultValue,
        defined,
        defineProperties,
        destroyObject,
        DeveloperError,
        Event) {
    "use strict";

    /**
     * A collection of {@link DataSource} instances.
     * @alias DataSourceCollection
     * @constructor
     */
    var DataSourceCollection = function() {
        this._dataSources = [];
        this._dataSourceAdded = new Event();
        this._dataSourceRemoved = new Event();
    };

    defineProperties(DataSourceCollection.prototype, {
        /**
         * Gets the number of data sources in this collection.
         * @memberof DataSourceCollection.prototype
         * @type {Number}
         * @readonly
         */
        length : {
            get : function() {
                return this._dataSources.length;
            }
        },

        /**
         * An event that is raised when a data source is added to the collection.
         * Event handlers are passed the data source that was added.
         * @memberof DataSourceCollection.prototype
         * @type {Event}
         * @readonly
         */
        dataSourceAdded : {
            get : function() {
                return this._dataSourceAdded;
            }
        },

        /**
         * An event that is raised when a data source is removed from the collection.
         * Event handlers are passed the data source that was removed.
         * @memberof DataSourceCollection.prototype
         * @type {Event}
         * @readonly
         */
        dataSourceRemoved : {
            get : function() {
                return this._dataSourceRemoved;
            }
        }
    });

    /**
     * Adds a data source to the collection.
     *
     * @param {DataSource} dataSource The data source to add.
     */
    DataSourceCollection.prototype.add = function(dataSource) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(dataSource)) {
            throw new DeveloperError('dataSource is required.');
        }
        //>>includeEnd('debug');

        this._dataSources.push(dataSource);
        this._dataSourceAdded.raiseEvent(this, dataSource);
    };

    /**
     * Removes a data source from this collection, if present.
     *
     * @param {DataSource} dataSource The data source to remove.
     * @param {Boolean} [destroy=false] Whether to destroy the data source in addition to removing it.
     *
     * @returns {Boolean} true if the data source was in the collection and was removed,
     *                    false if the data source was not in the collection.
     */
    DataSourceCollection.prototype.remove = function(dataSource, destroy) {
        destroy = defaultValue(destroy, false);

        var index = this._dataSources.indexOf(dataSource);
        if (index !== -1) {
            this._dataSources.splice(index, 1);
            this._dataSourceRemoved.raiseEvent(this, dataSource);

            if (destroy && typeof dataSource.destroy === 'function') {
                dataSource.destroy();
            }

            return true;
        }

        return false;
    };

    /**
     * Removes all data sources from this collection.
     *
     * @param {Boolean} [destroy=false] whether to destroy the data sources in addition to removing them.
     */
    DataSourceCollection.prototype.removeAll = function(destroy) {
        destroy = defaultValue(destroy, false);

        var dataSources = this._dataSources;
        for (var i = 0, len = dataSources.length; i < len; ++i) {
            var dataSource = dataSources[i];
            this._dataSourceRemoved.raiseEvent(this, dataSource);

            if (destroy && typeof dataSource.destroy === 'function') {
                dataSource.destroy();
            }
        }
    };

    /**
     * Checks to see if the collection contains a given data source.
     *
     * @param {DataSource} dataSource The data source to check for.
     *
     * @returns {Boolean} true if the collection contains the data source, false otherwise.
     */
    DataSourceCollection.prototype.contains = function(dataSource) {
        return this.indexOf(dataSource) !== -1;
    };

    /**
     * Determines the index of a given data source in the collection.
     *
     * @param {DataSource} dataSource The data source to find the index of.
     *
     * @returns {Number} The index of the data source in the collection, or -1 if the data source does not exist in the collection.
     */
    DataSourceCollection.prototype.indexOf = function(dataSource) {
        return this._dataSources.indexOf(dataSource);
    };

    /**
     * Gets a data source by index from the collection.
     *
     * @param {Number} index the index to retrieve.
     */
    DataSourceCollection.prototype.get = function(index) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(index)) {
            throw new DeveloperError('index is required.');
        }
        //>>includeEnd('debug');

        return this._dataSources[index];
    };

    /**
     * Returns true if this object was destroyed; otherwise, false.
     * If this object was destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.
     *
     * @returns {Boolean} true if this object was destroyed; otherwise, false.
     *
     * @see DataSourceCollection#destroy
     */
    DataSourceCollection.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the resources held by all data sources in this collection.  Explicitly destroying this
     * object allows for deterministic release of WebGL resources, instead of relying on the garbage
     * collector. Once this object is destroyed, it should not be used; calling any function other than
     * isDestroyed will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (undefined) to the object as done in the example.
     *
     * @returns {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see DataSourceCollection#isDestroyed
     *
     * @example
     * dataSourceCollection = dataSourceCollection && dataSourceCollection.destroy();
     */
    DataSourceCollection.prototype.destroy = function() {
        this.removeAll(true);
        return destroyObject(this);
    };

    return DataSourceCollection;
});<|endoftext|>


<commit_before>var Promise = require('promise');
var log4js = require('log4js');
var logger = log4js.getLogger();
var SlackWebhook = require('slack-webhook');
var slack = process.env.SLACK_WEBHOOK ? new SlackWebhook(process.env.SLACK_WEBHOOK, {
  defaults: {
    username: 'JukeBot'
  }
}) : null;

module.exports = {
  addVideo: addVideo,
  sendAddMessages: sendAddMessages
};

function addVideo(video) {
  logger.info('Adding video ' + video.key)
  return new Promise(function(resolve, reject) {
    Video.findOne({
      playing: true
    }).exec(function(err, current) {
      if (!current) {
        video.startTime = new Date();
        video.playing = true;
        video.played = true;
        setTimeout(endCurrentVideo, video.durationSeconds * 1000);
        video.save(function() {
          resolve(video);
        });
      } else {
        resolve(video);
      }
    });
  });
}

function sendAddMessages(video) {
  return new Promise(function(resolve, reject) {
    Video.publishCreate(video);
    if (slack) {
      slack.send({
        text: '@' + video.user + ' added a song to the playlist' + (video.playing ? ' and it\'s playing now' : '') + '! <' + sails.config.serverUrl + '|Listen to JukeBot>',
        attachments: [formatSlackAttachment(video)]
      }).then(function() {
        resolve(video);
      });
    } else {
      resolve(video);
    }
  });
}

function formatSlackAttachment(video) {
  return {
    title: video.title,
    title_link: 'https://www.youtube.com/watch?v=' + video.key,
    thumb_url: video.thumbnail
  };
}

function endCurrentVideo() {
  Video.findOne({
    playing: true
  }).exec(function(err, current) {
    current.playing = false;
    current.save(function() {
      logger.info('Publishing end song ' + current.key);
      Video.publishUpdate(current.id, current);
      findNextVideo();
    });
  });
}

function findNextVideo() {
  Video.find({
    played: false
  }).sort('createdAt ASC').exec(function(err, upcoming) {
    if (upcoming.length > 0) {
      startVideo(upcoming[0]);
    }
  });
}

function startVideo(video) {
  video.playing = true;
  video.played = true;
  video.startTime = new Date();
  logger.info('Setting timeout');
  setTimeout(endCurrentVideo, video.durationSeconds * 1000);
  video.save(function() {
    logger.info('Stopping video ' + video.key);
    Video.publishUpdate(video.id, video);
    if (slack) {
      slack.send({
        text: '*' + video.title + '* is now playing! <' + sails.config.serverUrl + '|Listen to JukeBot>',
        'mrkdwn': true
      }).then(function() {
        logger.info('Started playing video ' + video.key);
      });
    } else {
      logger.info('Started playing video ' + video.key);
    }
  });
}
<commit_msg>Remove duplicate @ symbol in incoming Slack message<commit_after>var Promise = require('promise');
var log4js = require('log4js');
var logger = log4js.getLogger();
var SlackWebhook = require('slack-webhook');
var slack = process.env.SLACK_WEBHOOK ? new SlackWebhook(process.env.SLACK_WEBHOOK, {
  defaults: {
    username: 'JukeBot'
  }
}) : null;

module.exports = {
  addVideo: addVideo,
  sendAddMessages: sendAddMessages
};

function addVideo(video) {
  logger.info('Adding video ' + video.key)
  return new Promise(function(resolve, reject) {
    Video.findOne({
      playing: true
    }).exec(function(err, current) {
      if (!current) {
        video.startTime = new Date();
        video.playing = true;
        video.played = true;
        setTimeout(endCurrentVideo, video.durationSeconds * 1000);
        video.save(function() {
          resolve(video);
        });
      } else {
        resolve(video);
      }
    });
  });
}

function sendAddMessages(video) {
  return new Promise(function(resolve, reject) {
    Video.publishCreate(video);
    if (slack) {
      slack.send({
        text: video.user + ' added a song to the playlist' + (video.playing ? ' and it\'s playing now' : '') + '! <' + sails.config.serverUrl + '|Listen to JukeBot>',
        attachments: [formatSlackAttachment(video)]
      }).then(function() {
        resolve(video);
      });
    } else {
      resolve(video);
    }
  });
}

function formatSlackAttachment(video) {
  return {
    title: video.title,
    title_link: 'https://www.youtube.com/watch?v=' + video.key,
    thumb_url: video.thumbnail
  };
}

function endCurrentVideo() {
  Video.findOne({
    playing: true
  }).exec(function(err, current) {
    current.playing = false;
    current.save(function() {
      logger.info('Publishing end song ' + current.key);
      Video.publishUpdate(current.id, current);
      findNextVideo();
    });
  });
}

function findNextVideo() {
  Video.find({
    played: false
  }).sort('createdAt ASC').exec(function(err, upcoming) {
    if (upcoming.length > 0) {
      startVideo(upcoming[0]);
    }
  });
}

function startVideo(video) {
  video.playing = true;
  video.played = true;
  video.startTime = new Date();
  logger.info('Setting timeout');
  setTimeout(endCurrentVideo, video.durationSeconds * 1000);
  video.save(function() {
    logger.info('Stopping video ' + video.key);
    Video.publishUpdate(video.id, video);
    if (slack) {
      slack.send({
        text: '*' + video.title + '* is now playing! <' + sails.config.serverUrl + '|Listen to JukeBot>',
        'mrkdwn': true
      }).then(function() {
        logger.info('Started playing video ' + video.key);
      });
    } else {
      logger.info('Started playing video ' + video.key);
    }
  });
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import config from '../config/environment';
import FirebaseAdapter from 'emberfire/adapters/firebase';
import ActiveModelAdapter from 'active-model-adapter';

let adapt;

if(config.environment === 'production') {
  const { inject } = Ember;

  adapt = FirebaseAdapter.extend({
    firebase: inject.service(),
  });
}
else {
  adapt = ActiveModelAdapter.extend();
}

export default adapt;
<commit_msg>back to using firebase in dev mode too<commit_after>import Ember from 'ember';
import config from '../config/environment';
import FirebaseAdapter from 'emberfire/adapters/firebase';
import ActiveModelAdapter from 'active-model-adapter';

let adapt;

if(config.environment !== 'test') {
  const { inject } = Ember;

  adapt = FirebaseAdapter.extend({
    firebase: inject.service(),
  });
}
else {
  adapt = ActiveModelAdapter.extend();
}

export default adapt;
<|endoftext|>