<commit_before>// Generated by CoffeeScript 1.9.0
(function() {
  var $, Api, ClassificationSummary, Classifier, ClassifyPage, DecisionTree, Dialog, DrawingTask, FieldGuide, MiniTutorial, StackOfPages, SubjectViewer, User, currentConfig, loginDialog, signupDialog,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __hasProp = {}.hasOwnProperty,
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  Dialog = require('zooniverse/controllers/dialog');

  loginDialog = require('zooniverse/controllers/login-dialog');

  signupDialog = require('zooniverse/controllers/signup-dialog');

  Classifier = require('./classifier');

  MiniTutorial = require('./mini-tutorial');

  SubjectViewer = require('./subject-viewer');

  DecisionTree = require('zooniverse-decision-tree');

  ClassificationSummary = require('./classification-summary');

  DrawingTask = require('./tasks/drawing');

  FieldGuide = require('./field-guide');

  currentConfig = require('zooniverse-readymade/current-configuration');

  User = require('zooniverse/models/user');

  $ = window.jQuery;

  StackOfPages = require('stack-of-pages');

  Api = require('zooniverse/lib/api');

  ClassifyPage = (function(_super) {
    __extends(ClassifyPage, _super);

    ClassifyPage.prototype.START_TUTORIAL = "zooniverse-readymade:classifier:start_tutorial";

    ClassifyPage.prototype.targetSubjectID = '';

    ClassifyPage.prototype.workflow = 'untitled_workflow';

    ClassifyPage.prototype.tasks = null;

    ClassifyPage.prototype.firstTask = '';

    ClassifyPage.prototype.tutorial = null;

    ClassifyPage.prototype.tutorialSteps = null;

    ClassifyPage.prototype.examples = null;

    ClassifyPage.prototype.classificationsSubmitted = 0;

    ClassifyPage.prototype.loginPromptEvery = 5;

    ClassifyPage.prototype.className = Classifier.prototype.className + " readymade-classify-page";

    ClassifyPage.prototype.template = require('./templates/classify-page');

    ClassifyPage.prototype.elements = {
      '.readymade-no-more-subjects-message': 'noMoreSubjectsMessage',
      '.readymade-subject-viewer-container': 'subjectViewerContainer',
      '.readymade-decision-tree-container': 'decisionTreeContainer',
      '.readymade-summary-container': 'summaryContainer',
      '.readymade-field-guide-container': 'fieldGuideContainer'
    };

    function ClassifyPage() {
      ClassifyPage.__super__.constructor.apply(this, arguments);
      if (this.tutorialSteps != null) {
        this.tutorial = new MiniTutorial({
          steps: this.tutorialSteps
        });
        $(this.tutorial.el).on(this.tutorial.CLOSE_EVENT, function() {
          var _ref;
          return (_ref = User.current) != null ? _ref.setPreference('tutorial_done', true) : void 0;
        });
        this.el.append(this.tutorial.el);
      }
      this.subjectViewer = new SubjectViewer;
      this.subjectViewerContainer.append(this.subjectViewer.el);
      this.decisionTree = new DecisionTree({
        taskTypes: {
          radio: require('./tasks/radio'),
          checkbox: require('./tasks/checkbox'),
          button: require('./tasks/button'),
          filter: require('./tasks/filter'),
          drawing: DrawingTask
        },
        tasks: this.tasks,
        firstTask: this.firstTask || Object.keys(this.tasks)[0]
      });
      this.listenTo(this.decisionTree.el, this.decisionTree.LOAD_TASK, (function(_this) {
        return function(e) {
          _this.subjectViewer.setTool(null, null);
          return _this.subjectViewer.setTaskIndex(e.detail.index);
        };
      })(this));
      this.listenTo(this.decisionTree.el, DrawingTask.prototype.SELECT_TOOL, (function(_this) {
        return function(e) {
          return setTimeout(function() {
            var choice, tool, _ref;
            _ref = e.detail, tool = _ref.tool, choice = _ref.choice;
            return _this.subjectViewer.setTool(tool, choice);
          });
        };
      })(this));
      this.listenTo(this.decisionTree.el, this.decisionTree.COMPLETE, (function(_this) {
        return function() {
          return _this.finishSubject();
        };
      })(this));
      this.decisionTreeContainer.append(this.decisionTree.el);
      this.summary = new ClassificationSummary;
      this.summary.on(this.summary.DISMISS, (function(_this) {
        return function() {
          return _this.getNextSubject();
        };
      })(this));
      this.summaryContainer.append(this.summary.el);
      if (this.examples != null) {
        this.fieldGuide = new FieldGuide({
          examples: this.examples
        });
        this.fieldGuideContainer.append(this.fieldGuide.el);
      }
      this.el.on(StackOfPages.prototype.activateEvent, (function(_this) {
        return function() {
          return _this.onActivate.apply(_this, arguments);
        };
      })(this));
    }

    ClassifyPage.prototype.isUserScientist = function() {
      var project, result, talkUser;
      result = new $.Deferred;
      if (User.current != null) {
        project = Api.current.get("/projects/" + Api.current.project);
        talkUser = Api.current.get("/projects/penguin/talk/users/" + User.current.name);
        $.when(project, talkUser).then((function(_this) {
          return function(project, talkUser) {
            var details, projectRoles, _ref, _ref1, _ref2, _ref3, _ref4;
            projectRoles = (_ref = (_ref1 = talkUser.talk) != null ? (_ref2 = _ref1.roles) != null ? _ref2[project.id] : void 0 : void 0) != null ? _ref : [];
            details = {
              project: project.id,
              roles: projectRoles,
              scientist: __indexOf.call(projectRoles, 'scientist') >= 0,
              admin: __indexOf.call(projectRoles, 'admin') >= 0,
              'brian-c': (_ref3 = talkUser.name) === 'brian-c' || _ref3 === 'eatyourgreens'
            };
            if (typeof console !== "undefined" && console !== null) {
              console.log('Can you pick your own subject?', JSON.stringify(details, null, 2));
            }
            return result.resolve(__indexOf.call(projectRoles, 'scientist') >= 0 || __indexOf.call(projectRoles, 'admin') >= 0 || ((_ref4 = talkUser.name) === 'brian-c' || _ref4 === 'eatyourgreens'));
          };
        })(this));
      } else {
        result.resolve(false);
      }
      return result.promise();
    };

    ClassifyPage.prototype.onActivate = function(e) {
      var _ref;
      this.targetSubjectID = e.originalEvent.detail.subjectID;
      if (this.targetSubjectID) {
        if (this.targetSubjectID !== ((_ref = this.Subject.current) != null ? _ref.zooniverse_id : void 0)) {
          return this.getNextSubject();
        }
      }
    };

    ClassifyPage.prototype.onUserChange = function(user) {
      var tutorialDone, _ref, _ref1, _ref2;
      ClassifyPage.__super__.onUserChange.apply(this, arguments);
      this.classificationsSubmitted = 0;
      if (this.tutorial != null) {
        tutorialDone = user != null ? (_ref = user.project) != null ? _ref.tutorial_done : void 0 : void 0;
        if (tutorialDone == null) {
          tutorialDone = user != null ? (_ref1 = user.preferences) != null ? (_ref2 = _ref1[currentConfig.id]) != null ? _ref2.tutorial_done : void 0 : void 0 : void 0;
        }
        if (tutorialDone) {
          if (this.tutorial.el.hasAttribute('data-open')) {
            return this.tutorial.close();
          }
        } else {
          return this.startTutorial();
        }
      }
    };

    ClassifyPage.prototype.getNextSubject = function() {
      if (this.targetSubjectID) {
        return this.isUserScientist().then((function(_this) {
          return function(theyAre) {
            var request;
            if (theyAre) {
              request = Api.current.get("/projects/" + Api.current.project + "/subjects/" + _this.targetSubjectID);
              request.then(function(data) {
                var subject;
                subject = new _this.Subject(data);
                return subject.select();
              });
              return request.fail(function() {
                return alert("There's no subject with the ID " + _this.targetSubjectID + ".");
              });
            } else {
              alert('Sorry, only science team members can choose the subjects they classify.');
              return ClassifyPage.__super__.getNextSubject.apply(_this, arguments);
            }
          };
        })(this));
      } else {
        return ClassifyPage.__super__.getNextSubject.apply(this, arguments);
      }
    };

    ClassifyPage.prototype.startTutorial = function() {
      this.tutorial.goTo(0);
      this.tutorial.open();
      return this.trigger(this.START_TUTORIAL, this, this.tutorial);
    };

    ClassifyPage.prototype.onNoMoreSubjects = function() {
      this.noMoreSubjectsMessage.show();
      this.subjectViewerContainer.hide();
      this.decisionTreeContainer.hide();
      return this.summaryContainer.hide();
    };

    ClassifyPage.prototype.createClassification = function(subject) {
      ClassifyPage.__super__.createClassification.apply(this, arguments);
      if (subject.zooniverse_id === this.targetSubjectID) {
        return this.classification.set('chosen_subject', true);
      }
    };

    ClassifyPage.prototype.loadSubject = function(subject, callback) {
      var args;
      args = arguments;
      this.noMoreSubjectsMessage.hide();
      this.subjectViewerContainer.show();
      this.decisionTreeContainer.show();
      this.summaryContainer.hide();
      this.subjectViewer.loadSubject(subject, (function(_this) {
        return function() {
          return ClassifyPage.__super__.loadSubject.apply(_this, args);
        };
      })(this));
      return this.summary.loadSubject(subject);
    };

    ClassifyPage.prototype.loadClassification = function(classification, callback) {
      var args;
      args = arguments;
      this.decisionTree.reset();
      return this.subjectViewer.loadClassification(classification, (function(_this) {
        return function() {
          return ClassifyPage.__super__.loadClassification.apply(_this, args);
        };
      })(this));
    };

    ClassifyPage.prototype.showSummary = function() {
      this.decisionTreeContainer.hide();
      this.summaryContainer.show();
      return this.targetSubjectID = '';
    };

    ClassifyPage.prototype.sendClassification = function() {
      var annotation, _i, _len, _ref;
      this.classification.set('workflow', this.workflow);
      _ref = this.composeAnnotations();
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        annotation = _ref[_i];
        this.classification.annotate(annotation);
      }
      ClassifyPage.__super__.sendClassification.apply(this, arguments);
      this.classificationsSubmitted += 1;
      if (User.current == null) {
        if (this.classificationsSubmitted % this.loginPromptEvery === 0) {
          return this.promptToLogIn();
        }
      }
    };

    ClassifyPage.prototype.composeAnnotations = function() {
      var annotations, decisionTreeValues, key, keyAndValue, mark, task, value, _i, _j, _len, _len1, _ref;
      annotations = [];
      _ref = this.subjectViewer.markingSurface.tools;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        mark = _ref[_i].mark;
        task = annotations[mark._taskIndex];
        if (task != null) {
          task.addMark(mark);
        }
      }
      decisionTreeValues = this.decisionTree.getValues();
      for (_j = 0, _len1 = decisionTreeValues.length; _j < _len1; _j++) {
        keyAndValue = decisionTreeValues[_j];
        for (key in keyAndValue) {
          value = keyAndValue[key];
          annotations.push({
            key: key,
            value: value
          });
        }
      }
      return annotations;
    };

    ClassifyPage.prototype.promptToLogIn = function() {
      var prompt;
      prompt = new Dialog({
        warning: true,
        content: "You've submitted " + this.classificationsSubmitted + " classifications, but you're you're not logged in!\n
Please sign in so we can make better use of your work and give you credit when the data is published.
\n
\n  <button name=\"close-dialog\">No thanks</button>\n  <button name=\"register\">Register</button>\n  <button name=\"sign-in\">Sign in</button>\n
",
        events: $.extend({}, Dialog.prototype.events, {
          'click button[name="register"]': function() {
            this.hide();
            return signupDialog.show();
          },
          'click button[name="sign-in"]': function() {
            this.hide();
            return loginDialog.show();
          }
        }),
        hide: function() {
          Dialog.prototype.hide.call(this);
          return setTimeout(((function(_this) {
            return function() {
              return _this.destroy();
            };
          })(this)), 600);
        }
      });
      return prompt.show();
    };

    ClassifyPage.prototype.events = {
      'change input[name="favorite"]': function(e) {
        return this.classification.favorite = e.target.checked;
      },
      'click button[name="restart-tutorial"]': function() {
        return this.startTutorial();
      }
    };

    return ClassifyPage;

  })(Classifier);

  module.exports = ClassifyPage;

}).call(this);
<commit_msg>build js<commit_after>// Generated by CoffeeScript 1.9.0
(function() {
  var $, Api, ClassificationSummary, Classifier, ClassifyPage, DecisionTree, Dialog, DrawingTask, FieldGuide, MiniTutorial, StackOfPages, SubjectViewer, User, currentConfig, loginDialog, signupDialog,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __hasProp = {}.hasOwnProperty,
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  Dialog = require('zooniverse/controllers/dialog');

  loginDialog = require('zooniverse/controllers/login-dialog');

  signupDialog = require('zooniverse/controllers/signup-dialog');

  Classifier = require('./classifier');

  MiniTutorial = require('./mini-tutorial');

  SubjectViewer = require('./subject-viewer');

  DecisionTree = require('zooniverse-decision-tree');

  ClassificationSummary = require('./classification-summary');

  DrawingTask = require('./tasks/drawing');

  FieldGuide = require('./field-guide');

  currentConfig = require('zooniverse-readymade/current-configuration');

  User = require('zooniverse/models/user');

  $ = window.jQuery;

  StackOfPages = require('stack-of-pages');

  Api = require('zooniverse/lib/api');

  ClassifyPage = (function(_super) {
    __extends(ClassifyPage, _super);

    ClassifyPage.prototype.START_TUTORIAL = "zooniverse-readymade:classifier:start_tutorial";

    ClassifyPage.prototype.targetSubjectID = '';

    ClassifyPage.prototype.workflow = 'untitled_workflow';

    ClassifyPage.prototype.tasks = null;

    ClassifyPage.prototype.firstTask = '';

    ClassifyPage.prototype.tutorial = null;

    ClassifyPage.prototype.tutorialSteps = null;

    ClassifyPage.prototype.examples = null;

    ClassifyPage.prototype.classificationsSubmitted = 0;

    ClassifyPage.prototype.loginPromptEvery = 5;

    ClassifyPage.prototype.className = Classifier.prototype.className + " readymade-classify-page";

    ClassifyPage.prototype.template = require('./templates/classify-page');

    ClassifyPage.prototype.elements = {
      '.readymade-no-more-subjects-message': 'noMoreSubjectsMessage',
      '.readymade-subject-viewer-container': 'subjectViewerContainer',
      '.readymade-decision-tree-container': 'decisionTreeContainer',
      '.readymade-summary-container': 'summaryContainer',
      '.readymade-field-guide-container': 'fieldGuideContainer'
    };

    function ClassifyPage() {
      ClassifyPage.__super__.constructor.apply(this, arguments);
      if (this.tutorialSteps != null) {
        this.tutorial = new MiniTutorial({
          steps: this.tutorialSteps
        });
        $(this.tutorial.el).on(this.tutorial.CLOSE_EVENT, function() {
          var _ref;
          return (_ref = User.current) != null ? _ref.setPreference('tutorial_done', true) : void 0;
        });
        this.el.append(this.tutorial.el);
      }
      this.subjectViewer = new SubjectViewer;
      this.subjectViewerContainer.append(this.subjectViewer.el);
      this.decisionTree = new DecisionTree({
        taskTypes: {
          radio: require('./tasks/radio'),
          checkbox: require('./tasks/checkbox'),
          button: require('./tasks/button'),
          filter: require('./tasks/filter'),
          drawing: DrawingTask
        },
        tasks: this.tasks,
        firstTask: this.firstTask || Object.keys(this.tasks)[0]
      });
      this.listenTo(this.decisionTree.el, this.decisionTree.LOAD_TASK, (function(_this) {
        return function(e) {
          _this.subjectViewer.setTool(null, null);
          return _this.subjectViewer.setTaskIndex(e.detail.index);
        };
      })(this));
      this.listenTo(this.decisionTree.el, DrawingTask.prototype.SELECT_TOOL, (function(_this) {
        return function(e) {
          return setTimeout(function() {
            var choice, tool, _ref;
            _ref = e.detail, tool = _ref.tool, choice = _ref.choice;
            return _this.subjectViewer.setTool(tool, choice);
          });
        };
      })(this));
      this.listenTo(this.decisionTree.el, this.decisionTree.COMPLETE, (function(_this) {
        return function() {
          return _this.finishSubject();
        };
      })(this));
      this.decisionTreeContainer.append(this.decisionTree.el);
      this.summary = new ClassificationSummary;
      this.summary.on(this.summary.DISMISS, (function(_this) {
        return function() {
          return _this.getNextSubject();
        };
      })(this));
      this.summaryContainer.append(this.summary.el);
      if (this.examples != null) {
        this.fieldGuide = new FieldGuide({
          examples: this.examples
        });
        this.fieldGuideContainer.append(this.fieldGuide.el);
      }
      this.el.on(StackOfPages.prototype.activateEvent, (function(_this) {
        return function() {
          return _this.onActivate.apply(_this, arguments);
        };
      })(this));
    }

    ClassifyPage.prototype.isUserScientist = function() {
      var project, result, talkUser;
      result = new $.Deferred;
      if (User.current != null) {
        project = Api.current.get("/projects/" + Api.current.project);
        talkUser = Api.current.get("/projects/penguin/talk/users/" + User.current.name);
        $.when(project, talkUser).then((function(_this) {
          return function(project, talkUser) {
            var details, projectRoles, _ref, _ref1, _ref2, _ref3, _ref4;
            projectRoles = (_ref = (_ref1 = talkUser.talk) != null ? (_ref2 = _ref1.roles) != null ? _ref2[project.id] : void 0 : void 0) != null ? _ref : [];
            details = {
              project: project.id,
              roles: projectRoles,
              scientist: __indexOf.call(projectRoles, 'scientist') >= 0,
              admin: __indexOf.call(projectRoles, 'admin') >= 0,
              'brian-c': (_ref3 = talkUser.name) === 'brian-c' || _ref3 === 'eatyourgreens'
            };
            if (typeof console !== "undefined" && console !== null) {
              console.log('Can you pick your own subject?', JSON.stringify(details, null, 2));
            }
            return result.resolve(__indexOf.call(projectRoles, 'scientist') >= 0 || __indexOf.call(projectRoles, 'admin') >= 0 || ((_ref4 = talkUser.name) === 'brian-c' || _ref4 === 'eatyourgreens'));
          };
        })(this));
      } else {
        result.resolve(false);
      }
      return result.promise();
    };

    ClassifyPage.prototype.onActivate = function(e) {
      var _ref;
      this.targetSubjectID = e.originalEvent.detail.subjectID;
      if (this.targetSubjectID) {
        if (this.targetSubjectID !== ((_ref = this.Subject.current) != null ? _ref.zooniverse_id : void 0)) {
          return this.getNextSubject();
        }
      }
    };

    ClassifyPage.prototype.onUserChange = function(user) {
      var tutorialDone, _ref, _ref1, _ref2;
      ClassifyPage.__super__.onUserChange.apply(this, arguments);
      this.classificationsSubmitted = 0;
      if (this.tutorial != null) {
        tutorialDone = user != null ? (_ref = user.project) != null ? _ref.tutorial_done : void 0 : void 0;
        if (tutorialDone == null) {
          tutorialDone = user != null ? (_ref1 = user.preferences) != null ? (_ref2 = _ref1[currentConfig.id]) != null ? _ref2.tutorial_done : void 0 : void 0 : void 0;
        }
        if (tutorialDone) {
          if (this.tutorial.el.hasAttribute('data-open')) {
            return this.tutorial.close();
          }
        } else {
          return this.startTutorial();
        }
      }
    };

    ClassifyPage.prototype.getNextSubject = function() {
      if (this.targetSubjectID) {
        return this.isUserScientist().then((function(_this) {
          return function(theyAre) {
            var request;
            if (theyAre) {
              request = Api.current.get("/projects/" + Api.current.project + "/subjects/" + _this.targetSubjectID);
              request.then(function(data) {
                var subject;
                subject = new _this.Subject(data);
                return subject.select();
              });
              return request.fail(function() {
                return alert("There's no subject with the ID " + _this.targetSubjectID + ".");
              });
            } else {
              alert('Sorry, only science team members can choose the subjects they classify.');
              return ClassifyPage.__super__.getNextSubject.apply(_this, arguments);
            }
          };
        })(this));
      } else {
        return ClassifyPage.__super__.getNextSubject.apply(this, arguments);
      }
    };

    ClassifyPage.prototype.startTutorial = function() {
      this.tutorial.goTo(0);
      this.tutorial.open();
      return this.trigger(this.START_TUTORIAL, this, this.tutorial);
    };

    ClassifyPage.prototype.onNoMoreSubjects = function() {
      this.noMoreSubjectsMessage.show();
      this.subjectViewerContainer.hide();
      this.decisionTreeContainer.hide();
      return this.summaryContainer.hide();
    };

    ClassifyPage.prototype.createClassification = function(subject) {
      ClassifyPage.__super__.createClassification.apply(this, arguments);
      if (subject.zooniverse_id === this.targetSubjectID) {
        return this.classification.set('chosen_subject', true);
      }
    };

    ClassifyPage.prototype.loadSubject = function(subject, callback) {
      var args;
      args = arguments;
      this.noMoreSubjectsMessage.hide();
      this.subjectViewerContainer.show();
      this.decisionTreeContainer.show();
      this.summaryContainer.hide();
      this.subjectViewer.loadSubject(subject, (function(_this) {
        return function() {
          return ClassifyPage.__super__.loadSubject.apply(_this, args);
        };
      })(this));
      return this.summary.loadSubject(subject);
    };

    ClassifyPage.prototype.loadClassification = function(classification, callback) {
      var args;
      args = arguments;
      this.decisionTree.reset();
      return this.subjectViewer.loadClassification(classification, (function(_this) {
        return function() {
          return ClassifyPage.__super__.loadClassification.apply(_this, args);
        };
      })(this));
    };

    ClassifyPage.prototype.showSummary = function() {
      this.decisionTreeContainer.hide();
      this.summaryContainer.show();
      return this.targetSubjectID = '';
    };

    ClassifyPage.prototype.sendClassification = function() {
      var annotation, _i, _len, _ref;
      this.classification.set('workflow', this.workflow);
      _ref = this.composeAnnotations();
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        annotation = _ref[_i];
        this.classification.annotate(annotation);
      }
      ClassifyPage.__super__.sendClassification.apply(this, arguments);
      this.classificationsSubmitted += 1;
      if (User.current == null) {
        if (this.classificationsSubmitted % this.loginPromptEvery === 0) {
          return this.promptToLogIn();
        }
      }
    };

    ClassifyPage.prototype.composeAnnotations = function() {
      var annotations, decisionTreeValues, key, keyAndValue, mark, task, value, _i, _j, _len, _len1, _ref;
      annotations = [];
      decisionTreeValues = this.decisionTree.getValues();
      for (_i = 0, _len = decisionTreeValues.length; _i < _len; _i++) {
        keyAndValue = decisionTreeValues[_i];
        for (key in keyAndValue) {
          value = keyAndValue[key];
          annotations.push({
            key: key,
            value: value
          });
        }
      }
      _ref = this.subjectViewer.markingSurface.tools;
      for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
        mark = _ref[_j].mark;
        task = annotations[mark._taskIndex];
        if (task != null) {
          task.value.push(mark);
        }
      }
      return annotations;
    };

    ClassifyPage.prototype.promptToLogIn = function() {
      var prompt;
      prompt = new Dialog({
        warning: true,
        content: "You've submitted " + this.classificationsSubmitted + " classifications, but you're you're not logged in!\n
Please sign in so we can make better use of your work and give you credit when the data is published.
\n
\n  <button name=\"close-dialog\">No thanks</button>\n  <button name=\"register\">Register</button>\n  <button name=\"sign-in\">Sign in</button>\n
",
        events: $.extend({}, Dialog.prototype.events, {
          'click button[name="register"]': function() {
            this.hide();
            return signupDialog.show();
          },
          'click button[name="sign-in"]': function() {
            this.hide();
            return loginDialog.show();
          }
        }),
        hide: function() {
          Dialog.prototype.hide.call(this);
          return setTimeout(((function(_this) {
            return function() {
              return _this.destroy();
            };
          })(this)), 600);
        }
      });
      return prompt.show();
    };

    ClassifyPage.prototype.events = {
      'change input[name="favorite"]': function(e) {
        return this.classification.favorite = e.target.checked;
      },
      'click button[name="restart-tutorial"]': function() {
        return this.startTutorial();
      }
    };

    return ClassifyPage;

  })(Classifier);

  module.exports = ClassifyPage;

}).call(this);
<|endoftext|>


<commit_before>var fse = require('fs-extra');
var path = require('path');
var fileUtils = require('../fileUtils');
var gitRepo = require('../gitRepo');
var utils = require('../utils');
var AffianceError = require('../error');
var Logger = require('../Logger');
var childProcess = require('child_process');
var configLoader = require('../config/loader');

var TEMPLATE_DIR = path.resolve(__dirname, '../../template-dir');
var MASTER_HOOK = path.join(TEMPLATE_DIR, 'hooks', 'affiance-hook');
var MASTER_HOOK_JS = path.join(TEMPLATE_DIR, 'hooks', 'affiance-hook.js');

var DEFAULT_OPTIONS = {
  action: 'install',
  force: false,
  hookToSign: null,
  target: gitRepo.repoRoot(),
  update: false
};

function Installer(logger, options) {
  this.logger = logger;
  this.options = utils.mergeOptions(options, DEFAULT_OPTIONS);
}

Installer.prototype.run = function() {
  this.validateTarget();

  try {
    switch(this.options.action) {
      case 'install':
        return this.install();
      case 'uninstall':
        return this.uninstall();
      case 'update':
        return this.update();
      case 'sign':
        return this.sign();
      default:
        return this.logger.error('Unknown Installer action:', this.options.action);
    }
  } catch(e) {
    switch(e.affianceName) {
      case AffianceError.PreExistingHooks:
        this.logger.error(e.message);
        process.exit(73) // EX_CANTCREAT

      case AffianceError.InvalidGitRepo:
        this.logger.error(e.message);
        process.exit(69) // EX_UNAVAILABLE

      default:
        this.logger.error(e.message);
        if (e.stack) { this.logger.debug(e.stack); }
        process.exit(70); // EX_SOFTWARE
    }
  }
};

Installer.prototype.install = function() {
  this.logger.log("Installing hooks into", this.options.target);

  this.ensureDirectory(this.hooksPath());
  this.preserveOldHooks();
  this.installMasterHook();
  this.installHookFiles();
  this.installStarterConfig();

  this.logger.success('Successfully installed hooks into', this.options.target);
  return true;
};

Installer.prototype.uninstall = function() {
  this.logger.log('Removing hooks from', this.options.target);

  this.uninstallHookFiles();
  this.uninstallMasterHook();
  this.uninstallMasterHook();
  this.restoreOldHooks();

  this.logger.success('Successfully removed hooks from', this.options.target);
  return true;
};

Installer.prototype.update = function() {
  if (this.compareFiles(MASTER_HOOK, this.masterHookInstallPath()) &&
    this.compareFiles(MASTER_HOOK_JS, this.masterHookJsInstallPath())) {
    // The installation is up to date.
    return false;
  }

  this.preserveOldHooks();
  this.installMasterHook();
  this.installHookFiles();
  this.logger.success('Hooks updated to Affiance version', utils.currentVersion());
  return true;
};

Installer.prototype.sign = function() {
  if (this.options.hookToSign) {
    this.signHook();
  } else {
    this.signConfig();
  }

  return true;
};

Installer.prototype.signHook = function() {
  this.logger.log('Updating signature for hook', this.options.hookToSign);
  var config = require('../config/loader').loadRepoConfig();

  var HookContext = require('../hook-context');
  var context = HookContext.createContext(this.options.hookToSign, config, process.argv.slice(1), process.stdin)

  var PluginHookLoader = require('../hook-loader/PluginHookLoader');
  new PluginHookLoader(config, context, this.logger).updateSignatures();
};

Installer.prototype.signConfig = function() {
  this.logger.log('Updating signature for config file');
  var config = require('../config/loader').loadRepoConfig({verify: false});
  config.updateSignature();
};

Installer.prototype.compareFiles = function(fileNameA, fileNameB) {
  var statA = fse.statSync(fileNameA);
  var statB = fse.statSync(fileNameB);

  // First check size;
  if(statA.size !== statB.size) { return false; }

  var contentA = fse.readFileSync(fileNameA, 'utf8');
  var contentB = fse.readFileSync(fileNameB, 'utf8');
  return (contentA === contentB);
};

Installer.prototype.validateTarget = function() {
  var absoluteTarget = fileUtils.absolutePath(this.options.target);
  if (!fse.existsSync(absoluteTarget)) {
    throw new AffianceError.error(AffianceError.InvalidGitRepo, 'target does not exist');
  }
  var stats = fse.statSync(absoluteTarget);
  if (!stats.isDirectory()) {
    throw new AffianceError.error(AffianceError.InvalidGitRepo, 'target not a directory');
  }

  var gitResult = childProcess.execSync('git rev-parse --git-dir', { cwd: absoluteTarget });
  if (gitResult === false) {
    throw new AffianceError.error(AffianceError.InvalidGitRepo, 'target does not appear to be a git repo');
  }
};

Installer.prototype.ensureDirectory = function(dirPath) {
  fse.ensureDirSync(dirPath);
};

Installer.prototype.preserveOldHooks = function() {
  this.ensureDirectory(this.oldHooksPath());

  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    var oldHookFileName = path.join(this.oldHooksPath(), hookType);
    if (!this.canReplaceFile(hookFileName)) {
      this.logger.warn('Hook ' + hookFileName + ' already exists and was not installed by Affiance. Moving to ' + this.oldHooksPath());
      fse.renameSync(hookFileName, oldHookFileName);
    }
  }
  // Clean up old-hooks directory if empty
  this.rmdirIfEmpty(this.oldHooksPath());
};

Installer.prototype.restoreOldHooks = function() {
  if (!fileUtils.isDirectory(this.oldHooksPath())) { return; }

  this.logger.log('Restoring old hooks from', this.oldHooksPath());

  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    var oldHookFileName = path.join(this.oldHooksPath(), hookType);
    if (fileUtils.isFile(oldHookFileName)) {
      fse.renameSync(oldHookFileName, hookFileName);
    }
  }
  // Clean up old-hooks directory if empty
  this.rmdirIfEmpty(this.oldHooksPath());

  this.logger.success('Successfully restored hooks from', this.oldHooksPath());
};

Installer.prototype.installMasterHook = function() {
  this.ensureDirectory(this.hooksPath());
  fse.copySync(MASTER_HOOK_JS, this.masterHookJsInstallPath());
  fse.copySync(MASTER_HOOK, this.masterHookInstallPath());
};

Installer.prototype.uninstallMasterHook = function() {
  fse.removeSync(this.masterHookInstallPath());
  fse.removeSync(this.masterHookJsInstallPath());
};

Installer.prototype.installHookFiles = function() {
  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    if (!this.canReplaceFile(hookFileName)) {
      throw new AffianceError.error(
        AffianceError.PreExistingHooks,
        'Hook' + hookFileName + 'already exists and was not installed by Affiance'
      );
    }
    fse.removeSync(hookFileName);
    fse.copySync(this.masterHookInstallPath(), hookFileName);
  }
};

Installer.prototype.uninstallHookFiles = function() {
  if (!fileUtils.isDirectory(this.hooksPath())) { return; }

  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    if (this.isAffianceHook(hookFileName)) {
      fse.removeSync(hookFileName);
    }
  }
};

Installer.prototype.installStarterConfig = function() {
  var repoConfigFile = path.join(this.options.target, configLoader.CONFIG_FILE_NAME);
  if (fse.existsSync(repoConfigFile)) { return; }

  var starterConfigPath = path.resolve(__dirname, '../../config/starter.yml');
  fse.copySync(starterConfigPath, repoConfigFile);
};

Installer.prototype.rmdirIfEmpty = function(dirPath) {
  try {
    fse.rmdirSync(dirPath);
  } catch(e) {
    // Rethrow error if code is not the expected 'ENOTEMPTY'
    if (e.code !== 'ENOTEMPTY') {
      throw(e);
    }
  }
};

Installer.prototype.hooksPath = function() {
  if (!this._hooksPath) {
    var absoluteTarget = fileUtils.absolutePath(this.options.target);
    var gitDir = gitRepo.gitDir(absoluteTarget);
    this._hooksPath = path.join(gitDir, 'hooks');
  }

  return this._hooksPath;
};

Installer.prototype.oldHooksPath = function() {
  if (!this._oldHooksPath) {
    this._oldHooksPath = path.join(this.hooksPath(), 'old-hooks');
  }

  return this._oldHooksPath;
};

Installer.prototype.masterHookInstallPath = function() {
  if (!this._masterHookInstallPath) {
    this._masterHookInstallPath = path.join(this.hooksPath(), 'affiance-hook');
  }
  return this._masterHookInstallPath;
};

Installer.prototype.masterHookJsInstallPath = function() {
  if (!this._masterHookJsInstallPath) {
    this._masterHookJsInstallPath = path.join(this.hooksPath(), 'affiance-hook.js');
  }
  return this._masterHookJsInstallPath;
};

Installer.prototype.canReplaceFile = function(fileName) {
  return this.options.force || !fse.existsSync(fileName) || this.isAffianceHook(fileName);
};

Installer.prototype.isAffianceHook = function(fileName) {
  if (!fse.existsSync(fileName)) { return false; }

  var fileContents = fse.readFileSync(fileName, 'utf8');
  return !!fileContents.match(/Affiance/g);
};

module.exports = Installer;
<commit_msg>Use utils to execute rev-parse to avoid thrown error<commit_after>var fse = require('fs-extra');
var path = require('path');
var fileUtils = require('../fileUtils');
var gitRepo = require('../gitRepo');
var utils = require('../utils');
var AffianceError = require('../error');
var Logger = require('../Logger');
var childProcess = require('child_process');
var configLoader = require('../config/loader');

var TEMPLATE_DIR = path.resolve(__dirname, '../../template-dir');
var MASTER_HOOK = path.join(TEMPLATE_DIR, 'hooks', 'affiance-hook');
var MASTER_HOOK_JS = path.join(TEMPLATE_DIR, 'hooks', 'affiance-hook.js');

var DEFAULT_OPTIONS = {
  action: 'install',
  force: false,
  hookToSign: null,
  target: gitRepo.repoRoot(),
  update: false
};

function Installer(logger, options) {
  this.logger = logger;
  this.options = utils.mergeOptions(options, DEFAULT_OPTIONS);
}

Installer.prototype.run = function() {
  this.validateTarget();

  try {
    switch(this.options.action) {
      case 'install':
        return this.install();
      case 'uninstall':
        return this.uninstall();
      case 'update':
        return this.update();
      case 'sign':
        return this.sign();
      default:
        return this.logger.error('Unknown Installer action:', this.options.action);
    }
  } catch(e) {
    switch(e.affianceName) {
      case AffianceError.PreExistingHooks:
        this.logger.error(e.message);
        process.exit(73) // EX_CANTCREAT

      case AffianceError.InvalidGitRepo:
        this.logger.error(e.message);
        process.exit(69) // EX_UNAVAILABLE

      default:
        this.logger.error(e.message);
        if (e.stack) { this.logger.debug(e.stack); }
        process.exit(70); // EX_SOFTWARE
    }
  }
};

Installer.prototype.install = function() {
  this.logger.log("Installing hooks into", this.options.target);

  this.ensureDirectory(this.hooksPath());
  this.preserveOldHooks();
  this.installMasterHook();
  this.installHookFiles();
  this.installStarterConfig();

  this.logger.success('Successfully installed hooks into', this.options.target);
  return true;
};

Installer.prototype.uninstall = function() {
  this.logger.log('Removing hooks from', this.options.target);

  this.uninstallHookFiles();
  this.uninstallMasterHook();
  this.uninstallMasterHook();
  this.restoreOldHooks();

  this.logger.success('Successfully removed hooks from', this.options.target);
  return true;
};

Installer.prototype.update = function() {
  if (this.compareFiles(MASTER_HOOK, this.masterHookInstallPath()) &&
    this.compareFiles(MASTER_HOOK_JS, this.masterHookJsInstallPath())) {
    // The installation is up to date.
    return false;
  }

  this.preserveOldHooks();
  this.installMasterHook();
  this.installHookFiles();
  this.logger.success('Hooks updated to Affiance version', utils.currentVersion());
  return true;
};

Installer.prototype.sign = function() {
  if (this.options.hookToSign) {
    this.signHook();
  } else {
    this.signConfig();
  }

  return true;
};

Installer.prototype.signHook = function() {
  this.logger.log('Updating signature for hook', this.options.hookToSign);
  var config = require('../config/loader').loadRepoConfig();

  var HookContext = require('../hook-context');
  var context = HookContext.createContext(this.options.hookToSign, config, process.argv.slice(1), process.stdin)

  var PluginHookLoader = require('../hook-loader/PluginHookLoader');
  new PluginHookLoader(config, context, this.logger).updateSignatures();
};

Installer.prototype.signConfig = function() {
  this.logger.log('Updating signature for config file');
  var config = require('../config/loader').loadRepoConfig({verify: false});
  config.updateSignature();
};

Installer.prototype.compareFiles = function(fileNameA, fileNameB) {
  var statA = fse.statSync(fileNameA);
  var statB = fse.statSync(fileNameB);

  // First check size;
  if(statA.size !== statB.size) { return false; }

  var contentA = fse.readFileSync(fileNameA, 'utf8');
  var contentB = fse.readFileSync(fileNameB, 'utf8');
  return (contentA === contentB);
};

Installer.prototype.validateTarget = function() {
  var absoluteTarget = fileUtils.absolutePath(this.options.target);
  if (!fse.existsSync(absoluteTarget)) {
    throw new AffianceError.error(AffianceError.InvalidGitRepo, 'target does not exist');
  }
  var stats = fse.statSync(absoluteTarget);
  if (!stats.isDirectory()) {
    throw new AffianceError.error(AffianceError.InvalidGitRepo, 'target not a directory');
  }

  var gitResult = utils.execSync('git rev-parse --git-dir', { cwd: absoluteTarget });
  if (gitResult === false) {
    throw new AffianceError.error(AffianceError.InvalidGitRepo, 'target does not appear to be a git repo');
  }
};

Installer.prototype.ensureDirectory = function(dirPath) {
  fse.ensureDirSync(dirPath);
};

Installer.prototype.preserveOldHooks = function() {
  this.ensureDirectory(this.oldHooksPath());

  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    var oldHookFileName = path.join(this.oldHooksPath(), hookType);
    if (!this.canReplaceFile(hookFileName)) {
      this.logger.warn('Hook ' + hookFileName + ' already exists and was not installed by Affiance. Moving to ' + this.oldHooksPath());
      fse.renameSync(hookFileName, oldHookFileName);
    }
  }
  // Clean up old-hooks directory if empty
  this.rmdirIfEmpty(this.oldHooksPath());
};

Installer.prototype.restoreOldHooks = function() {
  if (!fileUtils.isDirectory(this.oldHooksPath())) { return; }

  this.logger.log('Restoring old hooks from', this.oldHooksPath());

  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    var oldHookFileName = path.join(this.oldHooksPath(), hookType);
    if (fileUtils.isFile(oldHookFileName)) {
      fse.renameSync(oldHookFileName, hookFileName);
    }
  }
  // Clean up old-hooks directory if empty
  this.rmdirIfEmpty(this.oldHooksPath());

  this.logger.success('Successfully restored hooks from', this.oldHooksPath());
};

Installer.prototype.installMasterHook = function() {
  this.ensureDirectory(this.hooksPath());
  fse.copySync(MASTER_HOOK_JS, this.masterHookJsInstallPath());
  fse.copySync(MASTER_HOOK, this.masterHookInstallPath());
};

Installer.prototype.uninstallMasterHook = function() {
  fse.removeSync(this.masterHookInstallPath());
  fse.removeSync(this.masterHookJsInstallPath());
};

Installer.prototype.installHookFiles = function() {
  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    if (!this.canReplaceFile(hookFileName)) {
      throw new AffianceError.error(
        AffianceError.PreExistingHooks,
        'Hook' + hookFileName + 'already exists and was not installed by Affiance'
      );
    }
    fse.removeSync(hookFileName);
    fse.copySync(this.masterHookInstallPath(), hookFileName);
  }
};

Installer.prototype.uninstallHookFiles = function() {
  if (!fileUtils.isDirectory(this.hooksPath())) { return; }

  for (var i in utils.supportedHookTypes) {
    var hookType = utils.supportedHookTypes[i];
    var hookFileName = path.join(this.hooksPath(), hookType);
    if (this.isAffianceHook(hookFileName)) {
      fse.removeSync(hookFileName);
    }
  }
};

Installer.prototype.installStarterConfig = function() {
  var repoConfigFile = path.join(this.options.target, configLoader.CONFIG_FILE_NAME);
  if (fse.existsSync(repoConfigFile)) { return; }

  var starterConfigPath = path.resolve(__dirname, '../../config/starter.yml');
  fse.copySync(starterConfigPath, repoConfigFile);
};

Installer.prototype.rmdirIfEmpty = function(dirPath) {
  try {
    fse.rmdirSync(dirPath);
  } catch(e) {
    // Rethrow error if code is not the expected 'ENOTEMPTY'
    if (e.code !== 'ENOTEMPTY') {
      throw(e);
    }
  }
};

Installer.prototype.hooksPath = function() {
  if (!this._hooksPath) {
    var absoluteTarget = fileUtils.absolutePath(this.options.target);
    var gitDir = gitRepo.gitDir(absoluteTarget);
    this._hooksPath = path.join(gitDir, 'hooks');
  }

  return this._hooksPath;
};

Installer.prototype.oldHooksPath = function() {
  if (!this._oldHooksPath) {
    this._oldHooksPath = path.join(this.hooksPath(), 'old-hooks');
  }

  return this._oldHooksPath;
};

Installer.prototype.masterHookInstallPath = function() {
  if (!this._masterHookInstallPath) {
    this._masterHookInstallPath = path.join(this.hooksPath(), 'affiance-hook');
  }
  return this._masterHookInstallPath;
};

Installer.prototype.masterHookJsInstallPath = function() {
  if (!this._masterHookJsInstallPath) {
    this._masterHookJsInstallPath = path.join(this.hooksPath(), 'affiance-hook.js');
  }
  return this._masterHookJsInstallPath;
};

Installer.prototype.canReplaceFile = function(fileName) {
  return this.options.force || !fse.existsSync(fileName) || this.isAffianceHook(fileName);
};

Installer.prototype.isAffianceHook = function(fileName) {
  if (!fse.existsSync(fileName)) { return false; }

  var fileContents = fse.readFileSync(fileName, 'utf8');
  return !!fileContents.match(/Affiance/g);
};

module.exports = Installer;
<|endoftext|>


<commit_before>// client.js - main client file that does most of the processing
'use strict';

var fs           = require('fs'),
    constants    = require('constants'),
    request      = require('request'),
    util         = require('util'),
    EventEmitter = require('events').EventEmitter,
    Client       = require('./client').Client,
    qs           = require('querystring'),
    async        = require('async'),
    path         = require('path'),
    flatten      = require('flatten'),
    pjson        = require('./../../package.json'),
    failCodes = {
      400: 'Bad Request',
      401: 'Not Authorized',
      403: 'Forbidden',
      404: 'Item not found',
      405: 'Method not Allowed',
      409: 'Conflict',
      422: 'Unprocessable Entity',   // zendesk sends this one back when you re-use an organization name
      429: 'Too Many Requests',
      500: 'Internal Server Error',
      503: 'Service Unavailable'
    },

Client = exports.Client = function (options) {
  this.options = options;
  this.userAgent = 'node-zendesk/' + pjson.version + ' (node/' + process.versions.node + ')';
  // Each client has its own cookies to isolate authentication between clients
  this._request = request.defaults({
    jar:      this.options.get('no-cookies') ? false : request.jar(),
    encoding: this.options.get('encoding') || null,
    timeout:  this.options.get('timeout')  || 240000,
    proxy:    this.options.get('proxy')    || null,
    secureOptions: constants.SSL_OP_NO_TLSv1_2
  });

  if (!this.jsonAPIName) {
    this.jsonAPIName = null;
  }
  if (!this.jsonAPIName2) {
    this.jsonAPIName2 = null;
  }
  if (!this.jsonAPIName3) {
    this.jsonAPIName3 = null;
  }

  if ('function' !== typeof this.options.get) {
    this.options.get = function (key) {
      return this[key];
    };
  }

};

util.inherits(Client, EventEmitter);

Client.prototype.request = function (method, uri) {
  var options, url, self = this, res, args = Array.prototype.slice.call(arguments),
      callback = args.pop(),
      body     = 'object' === typeof args[args.length - 1] && !Array.isArray(args[args.length - 1]) && args.pop(),
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      proxy    = this.options.get('proxy'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token');

  url = assembleUrl(self, uri);

  if (options) { // is this ever used?
    self.options.headers = options;
  } else {
    self.options.headers = {
      'Content-Type': 'application/json',
      'Accept':       'application/json',
      'User-Agent':   self.userAgent
    };
  }

  if (useOAuth) {// token is an oauth token obtained from OAuth2
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {// token is an API token obtained from the Zendesk Settings UI
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  self.options.uri = url;
  self.options.method = method || 'GET';

  if (body) {
    self.options.body = JSON.stringify(body);
  } else if ('GET' !== method && 'application/json' === self.options.headers['Content-Type']) {
    self.options.body = '{}';
  }

  if (proxy) {
    self.options.proxy = proxy;
  }

  self.emit('debug::request', self.options);
  console.log(self.options.body);
  return this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });
};

Client.prototype.requestAll = function (method, uri) {
  var args         = Array.prototype.slice.call(arguments),
      callback     = args.pop(),
      nextPage     = 'Not Null!',
      bodyList     = [],
      statusList   = [],
      responseList = [],
      resultList   = [],
      self         = this;

  return Client.prototype.request.apply(this, args.concat(function (error, status, body, response, result) {
    if (error) {
      return callback(error);
    }

    statusList.push(status);
    bodyList.push(body);
    responseList.push(response);
    resultList.push(result);
    nextPage = result.next_page;

    async.whilst(
      function () {return null !== nextPage && 'undefined' !==  typeof nextPage; },
      function (cb) {
        Client.prototype.request.apply(self, ['GET', nextPage, function (error, status, body, response, result) {
          if (error) {
            return cb(error);
          }

          statusList.push(status);
          bodyList.push(body);
          responseList.push(response);
          resultList.push(result);
          nextPage = result.next_page;
          cb(null);
        }]);
      },
      function (err) {
        if (err) {
          callback(err);
        } else {
          return callback(null, statusList, flatten(bodyList), responseList, resultList);
        }
      }
      );
  }));
};

Client.prototype.requestUpload = function (uri, file, callback) {
  var self     = this,
      out,
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token'),
      uploadOptions = self.options;

  self.options.uri = assembleUrl(self, uri);
  self.options.method = 'POST';

  self.options.headers = {};

  if (useOAuth) {
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  out = this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });

  fs.createReadStream(file).pipe(out); // pipe the file!

};

function checkRequestResponse(self, response, result, callback) {
  var statusCode, error, retryAfter, res;

  if (!result) { // should this really be an error?
    error = new Error('Zendesk returned an empty result');
    error.statusCode = 204;
    return callback(error);
  }
  try {
    statusCode = response.statusCode;
    res = JSON.parse(result);
  }
  catch (ex) {
     // Emit Errors
    self.emit('debug::error', {exception: ex, code: statusCode, request: self.options, result: result});
  }

  self.emit('debug::response', {statusCode: statusCode, result: result});

  retryAfter = response.headers['Retry-After'];
  if (retryAfter) {
    error = new Error('Zendesk rate limits 200 requests per minute');
    error.statusCode = 429;
    error.result = retryAfter;
    return callback(error);
  }

  if (failCodes[statusCode]) {
    error = new Error('Zendesk Error (' + statusCode + '): ' + failCodes[statusCode]);
    error.statusCode = statusCode;
    error.result = result;
    return callback(error);
  }

  return callback(null, res);
}

function requestCallback(self, err, response, result, callback) {
  if (err) {
    return callback(err);
  }

  checkRequestResponse(self, response, result, function(err, res) {
    if (err) {
      return callback(err);
    }
    var body = null;
    if (res) {
      if (!body && null !== self.jsonAPIName) {
        body = res[(self.jsonAPIName.toString())];
      }
      if (!body && null !== self.jsonAPIName2) {
        body = res[(self.jsonAPIName2.toString())];
      }
      if (!body && null !== self.jsonAPIName3) {
        body = res[(self.jsonAPIName3.toString())];
      }

      if (!body) {
        body = res;
      }
    } else {
      body = '';
    }

    return callback(null, response.statusCode, body, response, res);
  });

}

function assembleUrl(self, uri) {
  var lastElement,
      params = '';

  if ('object' === typeof uri && Array.isArray(uri)) {
    lastElement = uri.pop();
    if (lastElement) {
      // we have received an object ex. {"sort_by":"id","sort_order":"desc"}
      if ('object' === typeof lastElement) {
        params = '?' + qs.stringify(lastElement);
      }
      // we have received a query string ex. '?sort_by=id&sort_order=desc'
      else if (0 === lastElement.toString().indexOf('?')) {
        params = lastElement;
      }
      else {
        uri.push(lastElement);
      }
    }
    return self.options.get('remoteUri') + '/' + uri.join('/') + '.json' + params;
  }
  else if ('string' === typeof uri && uri.indexOf(self.options.get('remoteUri')) === -1) {
    return self.options.get('remoteUri') + uri;
  }
  else {
    return uri;
  }
}
<commit_msg>Added the ticket merging call<commit_after>// client.js - main client file that does most of the processing
'use strict';

var fs           = require('fs'),
    constants    = require('constants'),
    request      = require('request'),
    util         = require('util'),
    EventEmitter = require('events').EventEmitter,
    Client       = require('./client').Client,
    qs           = require('querystring'),
    async        = require('async'),
    path         = require('path'),
    flatten      = require('flatten'),
    pjson        = require('./../../package.json'),
    failCodes = {
      400: 'Bad Request',
      401: 'Not Authorized',
      403: 'Forbidden',
      404: 'Item not found',
      405: 'Method not Allowed',
      409: 'Conflict',
      422: 'Unprocessable Entity',   // zendesk sends this one back when you re-use an organization name
      429: 'Too Many Requests',
      500: 'Internal Server Error',
      503: 'Service Unavailable'
    },

Client = exports.Client = function (options) {
  this.options = options;
  this.userAgent = 'node-zendesk/' + pjson.version + ' (node/' + process.versions.node + ')';
  // Each client has its own cookies to isolate authentication between clients
  this._request = request.defaults({
    jar:      this.options.get('no-cookies') ? false : request.jar(),
    encoding: this.options.get('encoding') || null,
    timeout:  this.options.get('timeout')  || 240000,
    proxy:    this.options.get('proxy')    || null,
    secureOptions: constants.SSL_OP_NO_TLSv1_2
  });

  if (!this.jsonAPIName) {
    this.jsonAPIName = null;
  }
  if (!this.jsonAPIName2) {
    this.jsonAPIName2 = null;
  }
  if (!this.jsonAPIName3) {
    this.jsonAPIName3 = null;
  }

  if ('function' !== typeof this.options.get) {
    this.options.get = function (key) {
      return this[key];
    };
  }

};

util.inherits(Client, EventEmitter);

Client.prototype.request = function (method, uri) {
  var options, url, self = this, res, args = Array.prototype.slice.call(arguments),
      callback = args.pop(),
      body     = 'object' === typeof args[args.length - 1] && !Array.isArray(args[args.length - 1]) && args.pop(),
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      proxy    = this.options.get('proxy'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token');

  url = assembleUrl(self, uri);

  if (options) { // is this ever used?
    self.options.headers = options;
  } else {
    self.options.headers = {
      'Content-Type': 'application/json',
      'Accept':       'application/json',
      'User-Agent':   self.userAgent
    };
  }

  if (useOAuth) {// token is an oauth token obtained from OAuth2
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {// token is an API token obtained from the Zendesk Settings UI
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  self.options.uri = url;
  self.options.method = method || 'GET';

  if (body) {
    self.options.body = JSON.stringify(body);
  } else if ('GET' !== method && 'application/json' === self.options.headers['Content-Type']) {
    self.options.body = '{}';
  }

  if (proxy) {
    self.options.proxy = proxy;
  }

  self.emit('debug::request', self.options);

  return this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });
};

Client.prototype.requestAll = function (method, uri) {
  var args         = Array.prototype.slice.call(arguments),
      callback     = args.pop(),
      nextPage     = 'Not Null!',
      bodyList     = [],
      statusList   = [],
      responseList = [],
      resultList   = [],
      self         = this;

  return Client.prototype.request.apply(this, args.concat(function (error, status, body, response, result) {
    if (error) {
      return callback(error);
    }

    statusList.push(status);
    bodyList.push(body);
    responseList.push(response);
    resultList.push(result);
    nextPage = result.next_page;

    async.whilst(
      function () {return null !== nextPage && 'undefined' !==  typeof nextPage; },
      function (cb) {
        Client.prototype.request.apply(self, ['GET', nextPage, function (error, status, body, response, result) {
          if (error) {
            return cb(error);
          }

          statusList.push(status);
          bodyList.push(body);
          responseList.push(response);
          resultList.push(result);
          nextPage = result.next_page;
          cb(null);
        }]);
      },
      function (err) {
        if (err) {
          callback(err);
        } else {
          return callback(null, statusList, flatten(bodyList), responseList, resultList);
        }
      }
      );
  }));
};

Client.prototype.requestUpload = function (uri, file, callback) {
  var self     = this,
      out,
      auth     = this.options.get('password') ? ':' + this.options.get('password') : '/token:' + this.options.get('token'),
      encoded  = new Buffer(this.options.get('username') + auth).toString('base64'),
      useOAuth = this.options.get('oauth'),
      token    = this.options.get('token'),
      uploadOptions = self.options;

  self.options.uri = assembleUrl(self, uri);
  self.options.method = 'POST';

  self.options.headers = {};

  if (useOAuth) {
    self.options.headers.Authorization = 'Bearer ' + token;
  } else {
    self.options.headers.Authorization = 'Basic ' + encoded;
  }

  out = this._request(self.options, function (err, response, result) {
    requestCallback(self, err, response, result, callback);
  });

  fs.createReadStream(file).pipe(out); // pipe the file!

};

function checkRequestResponse(self, response, result, callback) {
  var statusCode, error, retryAfter, res;

  if (!result) { // should this really be an error?
    error = new Error('Zendesk returned an empty result');
    error.statusCode = 204;
    return callback(error);
  }
  try {
    statusCode = response.statusCode;
    res = JSON.parse(result);
  }
  catch (ex) {
     // Emit Errors
    self.emit('debug::error', {exception: ex, code: statusCode, request: self.options, result: result});
  }

  self.emit('debug::response', {statusCode: statusCode, result: result});

  retryAfter = response.headers['Retry-After'];
  if (retryAfter) {
    error = new Error('Zendesk rate limits 200 requests per minute');
    error.statusCode = 429;
    error.result = retryAfter;
    return callback(error);
  }

  if (failCodes[statusCode]) {
    error = new Error('Zendesk Error (' + statusCode + '): ' + failCodes[statusCode]);
    error.statusCode = statusCode;
    error.result = result;
    return callback(error);
  }

  return callback(null, res);
}

function requestCallback(self, err, response, result, callback) {
  if (err) {
    return callback(err);
  }

  checkRequestResponse(self, response, result, function(err, res) {
    if (err) {
      return callback(err);
    }
    var body = null;
    if (res) {
      if (!body && null !== self.jsonAPIName) {
        body = res[(self.jsonAPIName.toString())];
      }
      if (!body && null !== self.jsonAPIName2) {
        body = res[(self.jsonAPIName2.toString())];
      }
      if (!body && null !== self.jsonAPIName3) {
        body = res[(self.jsonAPIName3.toString())];
      }

      if (!body) {
        body = res;
      }
    } else {
      body = '';
    }

    return callback(null, response.statusCode, body, response, res);
  });

}

function assembleUrl(self, uri) {
  var lastElement,
      params = '';

  if ('object' === typeof uri && Array.isArray(uri)) {
    lastElement = uri.pop();
    if (lastElement) {
      // we have received an object ex. {"sort_by":"id","sort_order":"desc"}
      if ('object' === typeof lastElement) {
        params = '?' + qs.stringify(lastElement);
      }
      // we have received a query string ex. '?sort_by=id&sort_order=desc'
      else if (0 === lastElement.toString().indexOf('?')) {
        params = lastElement;
      }
      else {
        uri.push(lastElement);
      }
    }
    return self.options.get('remoteUri') + '/' + uri.join('/') + '.json' + params;
  }
  else if ('string' === typeof uri && uri.indexOf(self.options.get('remoteUri')) === -1) {
    return self.options.get('remoteUri') + uri;
  }
  else {
    return uri;
  }
}
<|endoftext|>


<commit_before>var CloudCmd, Util, DOM;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            Element,
            INPUT       = 'INPUT',
            CONFIG,
            Template,
            Notify      = DOM.Notify,
            Config      = this;

        function init() {
            Loading     = true;

            showLoad();
            Util.exec.series([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                },
                Config.show
            ]);
        }

        this.show               = function() {
            var prefix  = CloudCmd.PREFIX,
                exec    = Util.exec,
                funcs   = [
                    exec.with(DOM.Files.get, 'config-tmpl'),
                    exec.with(DOM.load.css, prefix + '/css/config.css')
                ];

            if (!Loading) {
                showLoad();
                exec.parallel(funcs, fillTemplate);
            }
        };

        function fillTemplate(error, template) {
            if (!Template)
                Template = template;

            DOM.Files.get('config', function(error, config) {
                var div, data, inputs, inputFirst,
                    focus, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(Template, obj);
                div     = DOM.load({
                    name        : 'div',
                    className   : 'config',
                    inner       : data,
                    attribute   : {
                        'data-name': 'js-config'
                    }
                });

                Element = div;

                inputs      = DOM.getByTag(INPUT, div);
                inputFirst  = inputs[0];

                if (inputFirst)
                    focus   = function() {
                        inputFirst.focus();
                    };

                Util.forEach(inputs, function(input) {
                    Events.add('change', input, onChange)
                          .addKey(input, onKey);
                });

                CloudCmd.View.show(div, {
                    autoSize: true,
                    afterShow: focus
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            Object.keys(config).forEach(function(name) {
                var item    = config[name],
                    isBool  = Util.type.boolean(item);

                if (isBool)
                    config[name] = setState(item);
            });
        }

        function setState(state) {
            var ret = '';

            if (state)
                ret = ' checked';

            return ret;
        }

        function onChange(event) {
            var data,
                Config  = DOM.RESTful.Config,
                obj     = {},
                el      = event.target,
                name    = el.id,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;

                if (el.id === 'localStorage')
                    onLocalStorageChange(data);
                else if (el.id === 'diff')
                    onLSChange(data);
                else if (el.id === 'buffer')
                    onLSChange(data);
                else if (el.id === 'dirStorage')
                    onLSChange(data);

                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            obj[name] = data;

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            CONFIG[name] = data;

            DOM.Files.set('config', CONFIG, function() {
                Config.write(obj);
            });
        }

        function onLocalStorageChange(checked) {
            var elDiff          = DOM.getById('diff', Element),
                elBuffer        = DOM.getById('buffer', Element),
                elDirStorage    = DOM.getById('dirStorage', Element),
                isChecked       = elDiff.checked || elBuffer.checked || elDirStorage.checked,
                msg             = 'Diff, Buffer and Directory Storage do not work without localStorage';

            if (!checked && isChecked) {
                alert(msg);

                elDiff.checked          =
                elBuffer.checked        =
                elDirStorage.checked   = false;

                onChange({
                    target: elDiff
                });

                onChange({
                    target: elBuffer
                });
            }
        }

        function onLSChange(checked) {
            var element = DOM.getById('localStorage', Element);

            if (!element.checked && checked) {
                onLocalStorageChange(element.checked);
            }

            return element.checked;
        }

        function onKey(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<commit_msg>chore(config) rm "\n"<commit_after>var CloudCmd, Util, DOM;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            Element,
            INPUT       = 'INPUT',
            CONFIG,
            Template,
            Notify      = DOM.Notify,
            Config      = this;

        function init() {
            Loading     = true;

            showLoad();
            Util.exec.series([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                },
                Config.show
            ]);
        }

        this.show               = function() {
            var prefix  = CloudCmd.PREFIX,
                exec    = Util.exec,
                funcs   = [
                    exec.with(DOM.Files.get, 'config-tmpl'),
                    exec.with(DOM.load.css, prefix + '/css/config.css')
                ];

            if (!Loading) {
                showLoad();
                exec.parallel(funcs, fillTemplate);
            }
        };

        function fillTemplate(error, template) {
            if (!Template)
                Template = template;

            DOM.Files.get('config', function(error, config) {
                var div, data, inputs, inputFirst,
                    focus, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(Template, obj);
                div     = DOM.load({
                    name        : 'div',
                    className   : 'config',
                    inner       : data,
                    attribute   : {
                        'data-name': 'js-config'
                    }
                });

                Element = div;

                inputs      = DOM.getByTag(INPUT, div);
                inputFirst  = inputs[0];

                if (inputFirst)
                    focus   = function() {
                        inputFirst.focus();
                    };

                Util.forEach(inputs, function(input) {
                    Events.add('change', input, onChange)
                          .addKey(input, onKey);
                });

                CloudCmd.View.show(div, {
                    autoSize: true,
                    afterShow: focus
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            Object.keys(config).forEach(function(name) {
                var item    = config[name],
                    isBool  = Util.type.boolean(item);

                if (isBool)
                    config[name] = setState(item);
            });
        }

        function setState(state) {
            var ret = '';

            if (state)
                ret = ' checked';

            return ret;
        }

        function onChange(event) {
            var data,
                Config  = DOM.RESTful.Config,
                obj     = {},
                el      = event.target,
                name    = el.id,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;

                if (el.id === 'localStorage')
                    onLocalStorageChange(data);
                else if (el.id === 'diff')
                    onLSChange(data);
                else if (el.id === 'buffer')
                    onLSChange(data);
                else if (el.id === 'dirStorage')
                    onLSChange(data);

                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            obj[name] = data;

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            CONFIG[name] = data;

            DOM.Files.set('config', CONFIG, function() {
                Config.write(obj);
            });
        }

        function onLocalStorageChange(checked) {
            var elDiff          = DOM.getById('diff', Element),
                elBuffer        = DOM.getById('buffer', Element),
                elDirStorage    = DOM.getById('dirStorage', Element),
                isChecked       = elDiff.checked || elBuffer.checked || elDirStorage.checked,
                msg             = 'Diff, Buffer and Directory Storage do not work without localStorage';

            if (!checked && isChecked) {
                alert(msg);

                elDiff.checked          =
                elBuffer.checked        =
                elDirStorage.checked   = false;

                onChange({
                    target: elDiff
                });

                onChange({
                    target: elBuffer
                });
            }
        }

        function onLSChange(checked) {
            var element = DOM.getById('localStorage', Element);

            if (!element.checked && checked) {
                onLocalStorageChange(element.checked);
            }

            return element.checked;
        }

        function onKey(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<|endoftext|>


<commit_before>var CloudCmd, Util, DOM;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            INPUT       = 'INPUT',
            CONFIG,
            TEMPLATE,
            Notify      = DOM.Notify,
            Config      = this;

        function init(pCallBack) {
            Loading     = true;

            showLoad();
            Util.loadOnLoad([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                },
                Config.show
            ]);
        }

        this.show               = function() {
            var funcs = [
                getTemplate,
                cssLoad
            ];

            if (!Loading) {
                showLoad();
                Util.asyncCall(funcs, fillTemplate);
            }
        };

        function cssLoad(callback) {
            DOM.cssLoad({
                src : '/css/config.css',
                func: Util.retExec(callback)
            });
        }

        function getTemplate(callback) {
            Util.ifExec(TEMPLATE, callback, function (execCall) {
                    DOM.ajax({
                    url     : '/html/config.html',
                    success :  function(data) {
                        TEMPLATE = data;
                        execCall();
                    },
                    error   : Images.showError
                });

            });
        }

        function fillTemplate() {
            CloudCmd.getConfig(function(config) {
                var i, n, div, data, li, param, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(TEMPLATE, obj);
                div     = DOM.anyload({
                    name        : 'div',
                    className   : 'config',
                    inner       : data.toString()
                });

                li      = DOM.getByTag(INPUT, div);
                n       = li.length;

                for (i = 0; i < n; i++) {
                    param = li[i];
                    Events.add('change', change, param);
                    Events.addKey(key, param);
                }

                Images.hideLoad();
                CloudCmd.View.show(div, null, {
                    autoSize: true
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            var name;

            for (name in config)
                if (Util.isBoolean(config[name]))
                    config[name] = setState(config[name]);
        }

        function setState(state) {
            var ret = "";

            if (state)
                ret = " checked";

            return ret;
        }

        function change(event) {
            var data,
                obj     = {},
                el      = event.target,
                name    = el.id,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;
                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            obj[name] = data;

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            CONFIG[name] = data;
            CloudCmd.setConfig(CONFIG);

            DOM.RESTful.config(obj);
        }

        function key(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<commit_msg>refactor(config) change -> onChange<commit_after>var CloudCmd, Util, DOM;

(function(CloudCmd, Util, DOM) {
    'use strict';

    CloudCmd.Config = ConfigProto;

    function ConfigProto() {
        var Loading     = true,
            Key         = CloudCmd.Key,
            Images      = DOM.Images,
            Events      = DOM.Events,
            showLoad    = Images.showLoad.bind(DOM, {
                top: true
            }),
            INPUT       = 'INPUT',
            CONFIG,
            TEMPLATE,
            Notify      = DOM.Notify,
            Config      = this;

        function init(pCallBack) {
            Loading     = true;

            showLoad();
            Util.loadOnLoad([
                CloudCmd.View,
                function(callback) {
                    Loading = false;
                    Util.exec(callback);
                },
                Config.show
            ]);
        }

        this.show               = function() {
            var funcs = [
                getTemplate,
                cssLoad
            ];

            if (!Loading) {
                showLoad();
                Util.asyncCall(funcs, fillTemplate);
            }
        };

        function cssLoad(callback) {
            DOM.cssLoad({
                src : '/css/config.css',
                func: Util.retExec(callback)
            });
        }

        function getTemplate(callback) {
            Util.ifExec(TEMPLATE, callback, function (execCall) {
                    DOM.ajax({
                    url     : '/html/config.html',
                    success :  function(data) {
                        TEMPLATE = data;
                        execCall();
                    },
                    error   : Images.showError
                });

            });
        }

        function fillTemplate() {
            CloudCmd.getConfig(function(config) {
                var i, n, div, data, li, param, obj;

                CONFIG  = config;
                obj     = Util.copyObj(CONFIG);
                changeConfig(obj);
                data    = Util.render(TEMPLATE, obj);
                div     = DOM.anyload({
                    name        : 'div',
                    className   : 'config',
                    inner       : data.toString()
                });

                li      = DOM.getByTag(INPUT, div);
                n       = li.length;

                for (i = 0; i < n; i++) {
                    param = li[i];
                    Events.add('change', onChange, param);
                    Events.addKey(key, param);
                }

                Images.hideLoad();
                CloudCmd.View.show(div, null, {
                    autoSize: true
                });
            });
        }

        this.hide               = function() {
            CloudCmd.View.hide();
        };

        function changeConfig(config) {
            var name;

            for (name in config)
                if (Util.isBoolean(config[name]))
                    config[name] = setState(config[name]);
        }

        function setState(state) {
            var ret = "";

            if (state)
                ret = " checked";

            return ret;
        }

        function onChange(event) {
            var data,
                obj     = {},
                el      = event.target,
                name    = el.id,
                type    = el.type;

            switch(type) {
            case 'checkbox':
                data = el.checked;
                break;
            case 'number':
                data = el.value - 0;
                break;
            default:
                data = el.value;
                break;
            }

            obj[name] = data;

            if (name === 'notifications') {
                if (data && !Notify.check())
                    Notify.request();
            }

            CONFIG[name] = data;
            CloudCmd.setConfig(CONFIG);

            DOM.RESTful.config(obj);
        }

        function key(event) {
            var keyCode = event.keyCode,
                ESC     = Key.ESC;

            if (keyCode === ESC)
                Config.hide();
        }

        init();
    }

})(CloudCmd, Util, DOM);
<|endoftext|>


<commit_before>var utils = require('./utils');
var sockets = {};
var streams = {};

exports.setup = setupSocket;
exports.stream = setupStream;

/**
 * Setup a stream to write over a socket.
 *
 * @public
 * @param {object} The stream object.
 * @param {string} The destination URL.
 */
function setupStream (stream, url) {

    // append parsed url on stream
    if (typeof url === 'string' && !(url = parseUrl(url))) {
        return console.error('Invalid socket url.');
    }

    // create stream id
    stream.id = stream.id || utils.uid(3);

    // save url data
    stream.url = url;

    // create or get a socket
    stream.socket = stream.socket || getSocket(url.host);

    // save session on stream
    stream.session = stream.socket.session;

    // send data to socket on data
    stream.data(send);

    // append custom end handler
    stream._end = end;

    // save stream in cache
    streams[stream.id] = stream;

    // save stream on socket, to end stream on socket end
    stream.socket.streams[stream.id] = stream;

    return stream;
}

/**
 * Get or create a new client Socket.
 *
 * @public
 * @param {string} The target host.
 */
function getSocket (host) {

    // get socket from cached connections
    if (sockets[host]) {
        return sockets[host];
    }

    // create new websocket connection
    var socket = sockets[host] = new WebSocket('ws://' + host + '/');
    socket._cacheKey = host;

    // setup and return socket
    return setupSocket(socket);
}

/**
 * Setup socket with handlers.
 *
 * @public
 * @param {object} The socket.
 */
function setupSocket (socket) {

    // stream refs
    socket.streams = {};
    socket._buffer = [];

    // session
    socket.session = socket.session || {};

    // setup socket handlers
    socket.onclose = closeHandler;
    socket.onerror = errorHandler;
    socket.onmessage = parseMessage;
    socket.onopen = openHandler;

    return socket;
}

/**
 * Parse a socket URL string.
 *
 * @public
 * @param {string} The destination URL.
 */
function parseUrl (url) {

    url = url.split('/');
    if (url.length < 2) {
        return;
    }

    var urlObject = {};
    if (url.length < 3) {
        urlObject = {
            // TODO server default host??
            host: global.location.host,
            inst: url[0],
            emit: url[1]
        };
    } else {
        urlObject = {
            host: url[0],
            inst: url[1],
            emit: url[2]
        };
    }

    return urlObject;
}

/**
 * Create and send a websocket message.
 *
 * @private
 * @param {object} The error object.
 * @param {object} The data object.
 */
function send (err, data) {

    // create message
    var message = [1, this.url.inst, this.url.emit, this.id, err ? err.toString() : 0];

    // add the data to the message
    if (typeof data !== 'undefined') {
        message[5] = data;
    }

    // encode message (string)
    try {
        message = JSON.stringify(message);
    } catch (err) {
        return console.error(err);
    }

    // buffer writes if socket is not ready yet
    if (this.socket.readyState !== 1) {
        return this.socket._buffer.push(message);
    }

    // send message over socket
    this.socket.send(message);
}

/**
 * Custom end handler.
 *
 * @private
 */
function end () {

    // remove stream from cache
    delete streams[this.id];

    // remove stream from socket
    delete this.socket.streams[this.id];
}

/**
 * Prase websocket messages.
 *
 * @public
 * @param {object} The message event object.
 */
function parseMessage (messageEvent) {

    var socket = messageEvent.target;
    var message = messageEvent.data;
    var role = (socket.session || {})[engine.session_role];

    // parse message
    // protocoll: [type, instance, event, id, err, data]

    // TODO broadcast (grouped connections)
    /*
      - how to define/create groups?
      - get group by name
      - get group by session id
      - get group by regexp pattern?
      - get group by role?
      - check group access with session
      - public groups
    */

    try {
        message = JSON.parse(message);
    } catch (error) {
        return console.error(error);
    }

    // extract data from parsed message
    var type = message[0];
    var instance = message[1];
    var event = message[2];
    var id = message[3];
    var err = message[4];
    var data = message[5];

    // check mandatory message values
    if (!event || !instance || !id) {

        // send error back
        message[4] = 'Invalid message.';
        message[5] = 0;
        return socket.send(JSON.stringify(message));
    }

    // get the link object
    var stream = streams[id];

    // write data to stream on write message
    if (type) {

        // create a new stream and emit to flow handlers
        if (!stream) {

            // get module instance
            if (!(instance = engine.instances[instance])) {

                // load an instance
                // TODO buffer messages for this instance (pause)
                engine.load(message[1], role, function (err, instance) {

                    if (err) {
                        // send error back
                        message[4] = err;
                        message[5] = 0;
                        return socket.send(JSON.stringify(message));
                    }

                    // create a new stream and save it in cache and write data
                    createStream(instance, event, id, socket, message[1]).write(err, data);
                });
                return;

            // check role and event access on server module instance
            } else if (!(utils.eventAccess(instance, role, event))) {

                // send error back
                message[4] = 'Access denied to instance: ' + message[1];
                message[5] = 0;
                return socket.send(JSON.stringify(message));
            }

            // create a new stream and save it in cache and write data
            stream = createStream(instance, event, id, socket, message[1]);
        }

        stream.write(err, data);

    // end stream on end message
    } else if (stream) {
        stream.end(err, data);
    }
}

function createStream (instance, event, id, socket, instanceName) {

    // create stream and save it to cache
    sockets[id] = setupStream(instance.flow(event, {
        id: id,
        socket: socket
    }), {
        inst: instanceName,
        emit: event
    });

    return sockets[id];
}

/**
 * Write bufferd data when a socket is connected.
 *
 * @public
 * @param {object} The open event object.
 */
function openHandler (openEvent) {
    var socket = openEvent.target;

    // send buffered writes to socket
    if (socket._buffer.length) {
        for (var i = 0, l = socket._buffer.length; i < l; ++i) {
            socket.send(socket._buffer[i]);
        }

        // empty buffer
        socket._buffer = [];
    }
}

/**
 * Remove socket from cache and end streams.
 *
 * @public
 * @param {object} The close event object.
 */
function closeHandler (closeEvent) {
    var socket = closeEvent.target;

    // remove socket from sockets cache
    delete sockets[socket._cacheKey];

    // end all streams on socket
    for (var stream in socket.streams) {
        socket.streams[stream].end();
    }
}

/**
 * Write socket errors to streams.
 *
 * @public
 * @param {object} The error event object.
 */
function errorHandler (errorEvent) {
    var socket = errorEvent.target;
    for (var stream in socket.streams) {
        stream.write(errorEvent.data);
    }
}
<commit_msg>on the client, save socket refs in engine global<commit_after>var utils = require('./utils');
var sockets = {};
var streams = {};

if (typeof engine !== 'undefined') {
    engine.sockets = sockets;
}

exports.setup = setupSocket;
exports.stream = setupStream;

/**
 * Setup a stream to write over a socket.
 *
 * @public
 * @param {object} The stream object.
 * @param {string} The destination URL.
 */
function setupStream (stream, url) {

    // append parsed url on stream
    if (typeof url === 'string' && !(url = parseUrl(url))) {
        return console.error('Invalid socket url.');
    }

    // create stream id
    stream.id = stream.id || utils.uid(3);

    // save url data
    stream.url = url;

    // create or get a socket
    stream.socket = stream.socket || getSocket(url.host);

    // save session on stream
    stream.session = stream.socket.session;

    // send data to socket on data
    stream.data(send);

    // append custom end handler
    stream._end = end;

    // save stream in cache
    streams[stream.id] = stream;

    // save stream on socket, to end stream on socket end
    stream.socket.streams[stream.id] = stream;

    return stream;
}

/**
 * Get or create a new client Socket.
 *
 * @public
 * @param {string} The target host.
 */
function getSocket (host) {

    // get socket from cached connections
    if (sockets[host]) {
        return sockets[host];
    }

    // create new websocket connection
    var socket = sockets[host] = new WebSocket('ws://' + host + '/');
    socket._cacheKey = host;

    // setup and return socket
    return setupSocket(socket);
}

/**
 * Setup socket with handlers.
 *
 * @public
 * @param {object} The socket.
 */
function setupSocket (socket) {

    // stream refs
    socket.streams = {};
    socket._buffer = [];

    // session
    socket.session = socket.session || {};

    // setup socket handlers
    socket.onclose = closeHandler;
    socket.onerror = errorHandler;
    socket.onmessage = parseMessage;
    socket.onopen = openHandler;

    return socket;
}

/**
 * Parse a socket URL string.
 *
 * @public
 * @param {string} The destination URL.
 */
function parseUrl (url) {

    url = url.split('/');
    if (url.length < 2) {
        return;
    }

    var urlObject = {};
    if (url.length < 3) {
        urlObject = {
            // TODO server default host??
            host: global.location.host,
            inst: url[0],
            emit: url[1]
        };
    } else {
        urlObject = {
            host: url[0],
            inst: url[1],
            emit: url[2]
        };
    }

    return urlObject;
}

/**
 * Create and send a websocket message.
 *
 * @private
 * @param {object} The error object.
 * @param {object} The data object.
 */
function send (err, data) {

    // create message
    var message = [1, this.url.inst, this.url.emit, this.id, err ? err.toString() : 0];

    // add the data to the message
    if (typeof data !== 'undefined') {
        message[5] = data;
    }

    // encode message (string)
    try {
        message = JSON.stringify(message);
    } catch (err) {
        return console.error(err);
    }

    // buffer writes if socket is not ready yet
    if (this.socket.readyState !== 1) {
        return this.socket._buffer.push(message);
    }

    // send message over socket
    this.socket.send(message);
}

/**
 * Custom end handler.
 *
 * @private
 */
function end () {

    // TODO send end signal

    // remove stream from cache
    delete streams[this.id];

    // remove stream from socket
    delete this.socket.streams[this.id];
}

/**
 * Prase websocket messages.
 *
 * @public
 * @param {object} The message event object.
 */
function parseMessage (messageEvent) {

    var socket = messageEvent.target;
    var message = messageEvent.data;
    var role = (socket.session || {})[engine.session_role];

    // parse message
    // protocoll: [type, instance, event, id, err, data]

    // TODO broadcast (grouped connections)
    /*
      - how to define/create groups?
      - get group by name
      - get group by session id
      - get group by regexp pattern?
      - get group by role?
      - check group access with session
      - public groups
    */

    try {
        message = JSON.parse(message);
    } catch (error) {
        return console.error(error);
    }

    // extract data from parsed message
    var type = message[0];
    var instance = message[1];
    var event = message[2];
    var id = message[3];
    var err = message[4];
    var data = message[5];

    // check mandatory message values
    if (!event || !instance || !id) {

        // send error back
        message[4] = 'Invalid message.';
        message[5] = 0;
        return socket.send(JSON.stringify(message));
    }

    // get the link object
    var stream = streams[id];

    // write data to stream on write message
    if (type) {

        // create a new stream and emit to flow handlers
        if (!stream) {

            // get module instance
            if (!(instance = engine.instances[instance])) {

                // load an instance
                // TODO buffer messages for this instance (pause)
                engine.load(message[1], role, function (err, instance) {

                    if (err) {
                        // send error back
                        message[4] = err;
                        message[5] = 0;
                        return socket.send(JSON.stringify(message));
                    }

                    // create a new stream and save it in cache and write data
                    createStream(instance, event, id, socket, message[1]).write(err, data);
                });
                return;

            // check role and event access on server module instance
            } else if (!(utils.eventAccess(instance, role, event))) {

                // send error back
                message[4] = 'Access denied to instance: ' + message[1];
                message[5] = 0;
                return socket.send(JSON.stringify(message));
            }

            // create a new stream and save it in cache and write data
            stream = createStream(instance, event, id, socket, message[1]);
        }

        stream.write(err, data);

    // end stream on end message
    } else if (stream) {
        stream.end(err, data);
    }
}

function createStream (instance, event, id, socket, instanceName) {

    // create stream and save it to cache
    sockets[id] = setupStream(instance.flow(event, {
        id: id,
        socket: socket
    }), {
        inst: instanceName,
        emit: event
    });

    return sockets[id];
}

/**
 * Write bufferd data when a socket is connected.
 *
 * @public
 * @param {object} The open event object.
 */
function openHandler (openEvent) {
    var socket = openEvent.target;

    // send buffered writes to socket
    if (socket._buffer.length) {
        for (var i = 0, l = socket._buffer.length; i < l; ++i) {
            socket.send(socket._buffer[i]);
        }

        // empty buffer
        socket._buffer = [];
    }
}

/**
 * Remove socket from cache and end streams.
 *
 * @public
 * @param {object} The close event object.
 */
function closeHandler (closeEvent) {
    var socket = closeEvent.target;

    // remove socket from sockets cache
    delete sockets[socket._cacheKey];

    // end all streams on socket
    for (var stream in socket.streams) {
        socket.streams[stream].end();
    }
}

/**
 * Write socket errors to streams.
 *
 * @public
 * @param {object} The error event object.
 */
function errorHandler (errorEvent) {
    var socket = errorEvent.target;
    for (var stream in socket.streams) {
        stream.write(errorEvent.data);
    }
}
<|endoftext|>


<commit_before>var isObject = require('../lang/isObject')
var longish  = require('../array/longish')
var pad      = require('../string/padRight')
var keys     = require('../object/keys')
var assert   = require('assert')

function alignKeys (obj) {
  assert(isObject(obj), 'expects an object as input')

  var keyz = keys(obj)
  var max  = longish(keyz).length
  var res  = {}

  var len = keyz.length
  var i = -1

  while (++i < len) {
    var key = keyz[i]
    var prop = pad(key, max, ' ')
    res[prop] = obj[key]
  }

  return res
}

module.exports = alignKeys
<commit_msg>update text/align-keys — use pad-right<commit_after>var isObject = require('../lang/isObject')
var longish  = require('../array/longish')
var pad      = require('../string/pad-right')
var keys     = require('../object/keys')
var assert   = require('assert')

function alignKeys (obj) {
  assert(isObject(obj), 'expects an object as input')

  var keyz = keys(obj)
  var max  = longish(keyz).length
  var res  = {}

  var len = keyz.length
  var i = -1

  while (++i < len) {
    var key = keyz[i]
    var prop = pad(key, max, ' ')
    res[prop] = obj[key]
  }

  return res
}

module.exports = alignKeys
<|endoftext|>