<commit_before>$(document).ready(function() {

var body = $('body');

$('fieldset.editor').each(function() {
    var fieldset = $(this);
    var textarea = $('textarea', this);

    editorUploadImage(fieldset, textarea);
    editorPreview(fieldset, textarea);
});

function editorUploadImage(fieldset, textarea) {
    var addMedia = $('.add-media', fieldset);
    var addMediaProgress = $('.add-media-progress', fieldset);
    var addMediaProgressBar = $('.bar', addMediaProgress);
    var uploadImage = $('button.upload-image', fieldset);
    var uploadFile = $('<input type="file" name="file">');
    var uploadFileForm = $('<form>').append(uploadFile);
    body.append(uploadFileForm);

    uploadImage.click(function() {
        uploadFile.click();
        return false;
    });
    uploadFile.change(function() {
        if(uploadFile.val()) {
            addMedia.hide();
            textarea.prop('disabled', true);
            addMediaProgress.show();
            uploadFileForm.ajaxSubmit({
                url: '/editor/upload-image/',
                type: 'POST',
                headers: {
                    'X-CSRFToken': $.cookie('csrftoken'),
                },
                uploadProgress: function(evt, pos, total, percent) {
                    addMediaProgressBar.css('width', percent + '%');
                },
                success: function(responseData) {
                    addMedia.show();
                    addMediaProgress.hide();
                    textarea.prop('disabled', false);
                    insertImageTag(responseData['imageId']);
                }
            });
        }
    });

    function insertImageTag(imageId) {
        var toInsert = '#image-' + imageId + '-full';
        var text = textarea.val();
        if(textarea[0].selectionStart) {
            var pos = textarea[0].selectionStart;
        } else {
            var pos = 0;
        }
        function lineStartAt(pos) {
            return (pos < 1 || pos > text.length || text[pos-1] == "\n");
        }
        // Move forward until we find a newline
        while(!lineStartAt(pos)) {
            pos++;
        }
        // Check if we need to add newline before
        if(!lineStartAt(pos-1)) {
            toInsert = '\n' + toInsert;
            if(!lineStartAt(pos-2)) {
                toInsert = '\n' + toInsert;
            }
        }
        // Check if we need to add a newline or two after
        if(!lineStartAt(pos+2)) {
            toInsert += '\n';
            if(!lineStartAt(pos+1)) {
                toInsert += '\n';
            }
        }
        textarea.val(text.substring(0, pos) + toInsert +
                text.substring(pos, text.length));
        textarea[0].selectionStart = textarea[0].selectionEnd = pos;
    }
}

function editorPreview(fieldset, textarea) {
    var preview = $('button.preview', fieldset);
    var modal = $('#editor-preview-modal');
    var modalBody = $('div.body', modal);
    preview.click(function() {
        var overlay = $('
');
        $('body').append(overlay);
        modal.show();
        $('button.close', modal).click(function() {
            modal.hide();
            overlay.remove();
        });
        $.ajax({
            url: '/editor/preview/',
            type: 'GET',
            data: {
                'body': textarea.val(),
            },
            headers: {
                'X-CSRFToken': $.cookie('csrftoken'),
            },
            success: function(responseData) {
                $('i.fa-spinner', modal).hide();
                modalBody.html(responseData['body']);
                $('figure', modalBody).each(function(i) {
                    editorPreviewImage(this, textarea, i);
                });
            }
        });
        return false;
    });
}

var imageRE = /^(#image-\d+-\w+ *)$/m;
function editorPreviewImage(figureElt, textarea, index) {
    $('button', figureElt).click(function() {
        figureElt.className = $(this).data('class');
        $('img', figureElt).attr('src', $(this).data('url'));
        // Insert tag in our textarea
        var textParts = textarea.val().split(imageRE);
        textParts[1 + index * 2] = $(this).data('tag');
        textarea.val(textParts.join(""));
    });
}

});
<commit_msg>Make the upload file form hidden<commit_after>$(document).ready(function() {

var body = $('body');

$('fieldset.editor').each(function() {
    var fieldset = $(this);
    var textarea = $('textarea', this);

    editorUploadImage(fieldset, textarea);
    editorPreview(fieldset, textarea);
});

function editorUploadImage(fieldset, textarea) {
    var addMedia = $('.add-media', fieldset);
    var addMediaProgress = $('.add-media-progress', fieldset);
    var addMediaProgressBar = $('.bar', addMediaProgress);
    var uploadImage = $('button.upload-image', fieldset);
    var uploadFile = $('<input type="file" name="file">');
    var uploadFileForm = $('<form>').append(uploadFile);
    body.append(uploadFileForm.hide());

    uploadImage.click(function() {
        uploadFile.click();
        return false;
    });
    uploadFile.change(function() {
        if(uploadFile.val()) {
            addMedia.hide();
            textarea.prop('disabled', true);
            addMediaProgress.show();
            uploadFileForm.ajaxSubmit({
                url: '/editor/upload-image/',
                type: 'POST',
                headers: {
                    'X-CSRFToken': $.cookie('csrftoken'),
                },
                uploadProgress: function(evt, pos, total, percent) {
                    addMediaProgressBar.css('width', percent + '%');
                },
                success: function(responseData) {
                    addMedia.show();
                    addMediaProgress.hide();
                    textarea.prop('disabled', false);
                    insertImageTag(responseData['imageId']);
                }
            });
        }
    });

    function insertImageTag(imageId) {
        var toInsert = '#image-' + imageId + '-full';
        var text = textarea.val();
        if(textarea[0].selectionStart) {
            var pos = textarea[0].selectionStart;
        } else {
            var pos = 0;
        }
        function lineStartAt(pos) {
            return (pos < 1 || pos > text.length || text[pos-1] == "\n");
        }
        // Move forward until we find a newline
        while(!lineStartAt(pos)) {
            pos++;
        }
        // Check if we need to add newline before
        if(!lineStartAt(pos-1)) {
            toInsert = '\n' + toInsert;
            if(!lineStartAt(pos-2)) {
                toInsert = '\n' + toInsert;
            }
        }
        // Check if we need to add a newline or two after
        if(!lineStartAt(pos+2)) {
            toInsert += '\n';
            if(!lineStartAt(pos+1)) {
                toInsert += '\n';
            }
        }
        textarea.val(text.substring(0, pos) + toInsert +
                text.substring(pos, text.length));
        textarea[0].selectionStart = textarea[0].selectionEnd = pos;
    }
}

function editorPreview(fieldset, textarea) {
    var preview = $('button.preview', fieldset);
    var modal = $('#editor-preview-modal');
    var modalBody = $('div.body', modal);
    preview.click(function() {
        var overlay = $('
');
        $('body').append(overlay);
        modal.show();
        $('button.close', modal).click(function() {
            modal.hide();
            overlay.remove();
        });
        $.ajax({
            url: '/editor/preview/',
            type: 'GET',
            data: {
                'body': textarea.val(),
            },
            headers: {
                'X-CSRFToken': $.cookie('csrftoken'),
            },
            success: function(responseData) {
                $('i.fa-spinner', modal).hide();
                modalBody.html(responseData['body']);
                $('figure', modalBody).each(function(i) {
                    editorPreviewImage(this, textarea, i);
                });
            }
        });
        return false;
    });
}

var imageRE = /^(#image-\d+-\w+ *)$/m;
function editorPreviewImage(figureElt, textarea, index) {
    $('button', figureElt).click(function() {
        figureElt.className = $(this).data('class');
        $('img', figureElt).attr('src', $(this).data('url'));
        // Insert tag in our textarea
        var textParts = textarea.val().split(imageRE);
        textParts[1 + index * 2] = $(this).data('tag');
        textarea.val(textParts.join(""));
    });
}

});
<|endoftext|>


<commit_before>/**
 * Constructor function with Content Type initialization.
 *
 * @param {Object} options The content type settings.
 */
ContentType = function (options) {

  /** Validate mandatory settings */
  check(options.ctid, String);
  check(options.collection, Mongo.Collection);
  check(options.collection._c2, Object);
  check(options.collection._c2._simpleSchema, SimpleSchema);

  // The unique identifier of this content type.
  this._ctid = options.ctid;

  // The kind of template to be used.
  this._theme = options.theme || 'default';

  // Default path prefix (defined per content type).
  this._basePath = options.base_path || '/admin/content';

  // Reference to the Mongo.Collection instance.
  this._collection = options.collection;

  // Public access to Index+CRUD routes.
  this.routes = {};

  // Store reactive display state.
  this.displays = {};

  // Store allowed values based on recieved options.
  this.options = {
    endpoints: options.endpoints || {},
    resources: options.resources || {},
    labels: options.labels || {}
  }

  // Validate mandatory data before we initialize the content type.
  check(this._ctid, String);
  check(this._theme, String);
  check(this._basePath, String);

  /** Content type initialization */
  this.initialize();
}

/**
 * Run the initialization procedures.
 */
ContentType.prototype.initialize = function () {
  var self = this;
  var endpoints = this._getEndPoints();

  _.each(endpoints, function (endpoint, key){
    check(endpoint.enabled, Boolean);

    if (endpoint.enabled) {
      // Extend default endpoint displays based on received options.
      var defaultDisplay = "default";
      var option = self.options.endpoints[key];

      if (Match.test(option, Object) && Match.test(option.display, String)){
        defaultDisplay = option.display;
      }
      self.displays[key] = new ReactiveVar(defaultDisplay);

      // Creates the Router routes for each Index+CRUD endpoint.
      self._setEndPoint(endpoint, key);
    }
  });
}

/**
 * Builds the router route, the wrapper template and set the reactive
 * helper used by the wrapper template to allow reactive display update.
 *
 * @param {String} key      The endpoint key.
 * @param {Object} endpoint The endpoint settings.
 */
ContentType.prototype._setEndPoint = function (endpoint, key) {
  check(endpoint, {
    enabled:    Boolean,
    display:    String,
    path:       String,
    name:       String,
    displays:   Object
  });

  check(key, String);
  check(ContentTypes.settings.router, String);

  var self = this;
  var templatePrefix = ContentTypes.settings.templatePrefix[key];
  var templateWrapperOf = templatePrefix+"_"+self._theme;
  var templateWrapperTo =  templatePrefix+"_"+self._theme+"_"+self._ctid;

  // Fallback when the theme wrapper was not implemented.
  // Should never happend but just in case.
  check(Template[templateWrapperOf], Blaze.Template);
  check(Template[templateWrapperOf].renderFunction, Function);

  Template[templateWrapperTo] = new Template(templateWrapperTo, Template[templateWrapperOf].renderFunction);

  // Once we are sure the template wrapper exist, we build the route
  // with a reactive template name to allow realtime display update.
  Template[templateWrapperTo].helpers({
    template: function () {
      var display = self.displays[key].get();
      var template = self._getTemplateDisplayName(key, templateWrapperOf, display);

      self._setTemplateHelpers(key, template, display);
      self._setTemplateEvents(key, template, display);

      return template;
    }
  });

  switch (ContentTypes.settings.router) {
    case 'iron_router':
      self.routes[key] = Router.route(endpoint.path, {
        name: endpoint.name,
        template: templateWrapperTo
      });
      break;
    case 'flow_router':
      // @todo: add support for Flow Router.
      break;
  }
}

/**
 * Attach helpers to Display Templates.
 *
 * @param  {String} key      The Endpoint key.
 * @param  {String} template The Meteor Template where we need to attach the helpers.
 * @param  {String} display  The Display id.
 */
ContentType.prototype._setTemplateHelpers = function (key, template, display) {
  check(key, String);
  check(template, String);
  check(display, String);
  check(Template[template], Blaze.Template);

  var self = this;
  var helpers = self._getTemplateHelpers(key);
  var option  = self.options.endpoints[key];

  // Extend display helpers based on recieved options.
  if (option && Match.test(option.displays, Object) && Match.test(option.displays[display], Match.ObjectIncluding({helpers: Object}))){
    helpers = _.extend(helpers, option.displays[display].helpers);
  }

  // Attach default helpers for the Content Type specific template.
  Template[template].helpers(helpers);
}

/**
 * Attach events to Display Templates.
 *
 * @param  {String} key      The Endpoint key.
 * @param  {String} template The Meteor Template where we need to attach the events.
 * @param  {String} display  The Display id.
 */
ContentType.prototype._setTemplateEvents = function (key, template, display) {
  check(key, String);
  check(template, String);
  check(display, String);
  check(Template[template], Blaze.Template);

  var self = this;
  var events = self._getTemplateEvents(key);
  var option  = self.options.endpoints[key];

  // Extend display events based on recieved options.
  if (option && Match.test(option.displays, Object) && Match.test(option.displays[display], Match.ObjectIncluding({events: Object}))){
    events = _.extend(events, option.displays[display].events);
  }

  // Attach default events for the Content Type specific template.
  Template[template].events(events);
}

/**
 * Generates a new
 * @param  {[type]} key             [description]
 * @param  {[type]} templateWrapper [description]
 * @param  {[type]} display         [description]
 * @return {[type]}                 [description]
 */
ContentType.prototype._getTemplateDisplayName = function (key, templateWrapper, display) {
  check(key, String);
  check(templateWrapper, String);
  check(display, String);

  var self = this;
  var copyOf  = templateWrapper+'_'+display;
  var copyTo  = templateWrapper+'_'+display+'_'+self._ctid;

  // Verify that origin template exists but if not we provide
  // default display as fallback.
  if(!Match.test(Template[copyOf], Blaze.Template)){
    copyOf = templateWrapper+'_default';
  }

  check(Template[copyOf].renderFunction, Function);

  // Duplicate the default template to make it Content Type specific.
  Template[copyTo] = new Template(copyTo, Template[copyOf].renderFunction);

  return copyTo;
}

/**
 * Getter function to return a single endpoint data.
 *
 * @param  {String} key The endpoint identifier.
 * @return {Object}     A single endpoint data.
 */
ContentType.prototype._getEndPoint = function (key) {
  check(key, String);

  var endpoints = this._getEndPoints();
  return endpoints[key];
}

/**
 * Store the out-of-the-box Index + CRUD endpoint esqueleton. The object
 * returned here will be used to build the Router routes.
 *
 * @return {Object} The basic information needed to build the routes.
 */
ContentType.prototype._getEndPoints = function () {
  var self = this;

  var endpoints = {
    index: {
      enabled: true,
      display: 'default',
      path: self._basePath+'/'+self._ctid+'/index',
      name: 'ct.'+self._ctid+'.index',
      displays: {
        default: {}
      }
    },
    create: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/create',
      name: 'ct.'+this._ctid+'.create',
      displays: {
        default: {}
      }
    },
    read: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/:_id',
      name: 'ct.'+this._ctid+'.read',
      displays: {
        default: {}
      }
    },
    update: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/:_id/edit',
      name: 'ct.'+this._ctid+'.update',
      displays: {
        default: {}
      }
    },
    delete: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/:_id/delete',
      name: 'ct.'+this._ctid+'.delete',
      displays: {
        default: {}
      }
    }
  }

  // @todo: check if this is still usefull.
  _.each(self.options.endpoints, function (endpoint, key) {
    if (Match.test(endpoint.enabled, Boolean)){
      endpoints[key].enabled = endpoint.enabled;
    }
  });

  return endpoints;
}

/**
 * Provide default helpers for Content Type templates.
 *
 * @param  {String} key The endpoint identifier.
 * @return {Object}     Meteor template helpers.
 */
ContentType.prototype._getTemplateHelpers = function (key) {
  check(key, String);

  var self = this;

  var helpers = {
    index: {
      meta: {
        title: 'Documents of type ' + self._ctid + ''
      },
      items: function () {
        var cursor = self._collection.find({});
        return {
          cursor: cursor,
          total: cursor.count()
        };
      }
    },
    create: {
      meta: {
        title: 'Create new ' + this._ctid + ''
      },
      formCollection: self._collection,
      formId: 'insert-form-'+self._ctid,
      formType: 'insert',
    },
    read: {
      meta: {
        title: 'View ' + this._ctid + ''
      },
      item: function () {
        var router = self.currentRoute();
        return self._collection.findOne({_id:router.params._id});
      }
    },
    update: {
      meta: {
        title: 'Update ' + this._ctid + ''
      },
      formCollection: self._collection,
      formId: 'update-form-'+self._ctid,
      formType: 'update',
      item: function () {
        var router = self.currentRoute();
        return self._collection.findOne({_id:router.params._id});
      }
    },
    delete: {
      meta: {
        title: 'Delete ' + this._ctid + ''
      },
      formCollection: self._collection,
      item: function () {
        var router = self.currentRoute();
        return self._collection.findOne({_id:router.params._id});
      }
    }
  };

  // Helpers common to all templates.
  helpers[key].ct = {
    fields: self._getSimpleSchemaFields(),
    labels: self._getTemplateLabels(),
    pathTo: {
      index:    'ct.'+self._ctid+'.index',
      create:   'ct.'+self._ctid+'.create',
      read:     'ct.'+self._ctid+'.read',
      update:   'ct.'+self._ctid+'.update',
      delete:   'ct.'+self._ctid+'.delete'
    }
  };

  return helpers[key];
}

/**
 * Provide default events for Content Type templates.
 *
 * @param  {String} key The endpoint identifier.
 * @return {Object}     Meteor template eventMap.
 */
ContentType.prototype._getTemplateEvents = function (key) {
  check(key, String);

  var self = this;

  var events = {
    index: {},
    create: {},
    read: {},
    update: {},
    delete: {}
  };

  return events[key];
}

/**
 * Label abstraction layer to let this package to be i18n friendly and to
 * provide custom labels in case you are using custom displays.
 *
 * @return {Object} Key-Value strings.
 */
ContentType.prototype._getTemplateLabels = function (labels) {
  var self = this;

  var labels = {
    backToIndex: "Back to Index",
    backToDocument: "Back to Document",
    confirmOk: "Yes, I'am sure",
    deletePrefix: "You are about to delete the document",
    deleteSuffix: "This action is unrecoverable. Are you sure?",
    linkView: "View",
    linkEdit: "Edit",
    linkDelete: "Delete",
    linkCreate: "Create new document",
    totalItemsPrefix: "Found",
    totalItemsSuffix: "item/s.",
    noItemsFound: "No documents found.",
    thKey: "Key",
    thLabel: "Label",
    thValue: "Value"
  }

  _.each(self.options.labels, function (label, key) {
    if (Match.test(label, String)){
      labels[key] = label;
    }
  });

  return labels;
}

/**
 * Builds an array with field keys and labels.
 *
 * @return {Object} An array with key-value objects.
 */
ContentType.prototype._getSimpleSchemaFields = function () {
  var self = this;

  return _.map(self._collection._c2._simpleSchema._schema, function(value, key){
    return {
      key: key,
      value: value.label
    };
  });
}

/**
 * Updates the display for specific endpoint.
 *
 * @param {String} key     The endpoint key.
 * @param {String} display The display name.
 */
ContentType.prototype.setDisplay = function (key, display) {
  check(key, String);
  check(display, String);
  check(this.displays[key], ReactiveVar);

  this.displays[key].set(display);
}

/**
 * Getter returning the current route with router abstraction.
 *
 * @return {Object} The current router route.
 */
ContentType.prototype.currentRoute = function () {
  var current = false;

  switch (ContentTypes.settings.router) {
    case 'iron_router':
      current = Router.current();
      break;
    case 'flow_router':
      current = Router.current();
      break;
  }
  return current;
}
<commit_msg>Allow to setup onCreated, onRendered and onDestroyed Template hooks when setting up a Display.<commit_after>/**
 * Constructor function with Content Type initialization.
 *
 * @param {Object} options The content type settings.
 */
ContentType = function (options) {

  /** Validate mandatory settings */
  check(options.ctid, String);
  check(options.collection, Mongo.Collection);
  check(options.collection._c2, Object);
  check(options.collection._c2._simpleSchema, SimpleSchema);

  // The unique identifier of this content type.
  this._ctid = options.ctid;

  // The kind of template to be used.
  this._theme = options.theme || 'default';

  // Default path prefix (defined per content type).
  this._basePath = options.base_path || '/admin/content';

  // Reference to the Mongo.Collection instance.
  this._collection = options.collection;

  // Public access to Index+CRUD routes.
  this.routes = {};

  // Store reactive display state.
  this.displays = {};

  // Define the default endpoints.
  this.defaultEndpoints = ['index', 'created', 'read', 'update', 'delete'];

  // Store allowed values based on recieved options.
  this.options = {
    endpoints: options.endpoints || {},
    resources: options.resources || {},
    labels: options.labels || {}
  }

  // Validate mandatory data before we initialize the content type.
  check(this._ctid, String);
  check(this._theme, String);
  check(this._basePath, String);

  /** Content type initialization */
  this.initialize();
}

/**
 * Run the initialization procedures.
 */
ContentType.prototype.initialize = function () {
  var self = this;
  var endpoints = this._getEndPoints();

  _.each(endpoints, function (endpoint, key){
    check(endpoint.enabled, Boolean);

    if (endpoint.enabled) {
      // Extend default endpoint displays based on received options.
      var defaultDisplay = "default";
      var option = self.options.endpoints[key];

      if (Match.test(option, Object) && Match.test(option.display, String)){
        defaultDisplay = option.display;
      }
      self.displays[key] = new ReactiveVar(defaultDisplay);

      // Creates the Router routes for each Index+CRUD endpoint.
      self._setEndPoint(endpoint, key);
    }
  });
}

/**
 * Builds the router route, the wrapper template and set the reactive
 * helper used by the wrapper template to allow reactive display update.
 *
 * @param {String} key      The endpoint key.
 * @param {Object} endpoint The endpoint settings.
 */
ContentType.prototype._setEndPoint = function (endpoint, key) {
  check(endpoint, {
    enabled:    Boolean,
    display:    String,
    path:       String,
    name:       String,
    displays:   Object
  });

  check(key, String);
  check(ContentTypes.settings.router, String);

  var self = this;
  var templatePrefix = ContentTypes.settings.templatePrefix[key];
  var templateWrapperOf = templatePrefix+"_"+self._theme;
  var templateWrapperTo =  templatePrefix+"_"+self._theme+"_"+self._ctid;

  // Fallback when the theme wrapper was not implemented.
  // Should never happend but just in case.
  check(Template[templateWrapperOf], Blaze.Template);
  check(Template[templateWrapperOf].renderFunction, Function);

  Template[templateWrapperTo] = new Template(templateWrapperTo, Template[templateWrapperOf].renderFunction);

  // Once we are sure the template wrapper exist, we build the route
  // with a reactive template name to allow realtime display update.
  Template[templateWrapperTo].helpers({
    template: function () {
      var display = self.displays[key].get();
      var template = self._getTemplateDisplayName(key, templateWrapperOf, display);

      self._setTemplateHooks(key, template, display);
      self._setTemplateHelpers(key, template, display);
      self._setTemplateEvents(key, template, display);

      return template;
    }
  });

  switch (ContentTypes.settings.router) {
    case 'iron_router':
      self.routes[key] = Router.route(endpoint.path, {
        name: endpoint.name,
        template: templateWrapperTo
      });
      break;
    case 'flow_router':
      // @todo: add support for Flow Router.
      break;
  }
}

/**
 * Attach helpers to Display Templates.
 *
 * @param  {String} key      The Endpoint key.
 * @param  {String} template The Meteor Template where we need to attach the helpers.
 * @param  {String} display  The Display id.
 */
ContentType.prototype._setTemplateHelpers = function (key, template, display) {
  check(key, String);
  check(template, String);
  check(display, String);
  check(Template[template], Blaze.Template);

  var self = this;
  var helpers = self._getTemplateHelpers(key);
  var option  = self.options.endpoints[key];

  // Extend display helpers based on recieved options.
  if (option && Match.test(option.displays, Object) && Match.test(option.displays[display], Match.ObjectIncluding({helpers: Object}))){
    helpers = _.extend(helpers, option.displays[display].helpers);
  }

  // Attach default helpers for the Content Type specific template.
  Template[template].helpers(helpers);
}

/**
 * Attach events to Display Templates.
 *
 * @param  {String} key      The Endpoint key.
 * @param  {String} template The Meteor Template where we need to attach the events.
 * @param  {String} display  The Display id.
 */
ContentType.prototype._setTemplateEvents = function (key, template, display) {
  check(key, String);
  check(template, String);
  check(display, String);
  check(Template[template], Blaze.Template);

  var self = this;
  var events = self._getTemplateEvents(key);
  var option  = self.options.endpoints[key];

  // Extend display events based on recieved options.
  if (option && Match.test(option.displays, Object) && Match.test(option.displays[display], Match.ObjectIncluding({events: Object}))){
    events = _.extend(events, option.displays[display].events);
  }

  // Attach default events for the Content Type specific template.
  Template[template].events(events);
}

/**
 * Attach hooks to Display Templates.
 *
 * @param  {String} key      The Endpoint key.
 * @param  {String} template The Meteor Template where we need to attach the events.
 * @param  {String} display  The Display id.
 */
ContentType.prototype._setTemplateHooks = function (key, template, display) {
  check(key, String);
  check(template, String);
  check(display, String);
  check(Template[template], Blaze.Template);

  var self = this;
  var option  = self.options.endpoints[key];

  // Extend display events based on recieved options.
  if (!option || !Match.test(option.displays, Object))
    return false;

  if(Match.test(option.displays[display], Match.ObjectIncluding({onCreated: Function})))
    Template[template].onCreated(option.displays[display].onCreated);

  if(Match.test(option.displays[display], Match.ObjectIncluding({onRendered: Function})))
    Template[template].onRendered(option.displays[display].onRendered);

  if(Match.test(option.displays[display], Match.ObjectIncluding({onDestroyed: Function})))
    Template[template].onDestroyed(option.displays[display].onDestroyed);
}

/**
 * Generates a new Template based on default Template provided by theme package.
 *
 * @param  {String} key             The endpoint identifier.
 * @param  {String} templateWrapper The Template name provided by theme package.
 * @param  {String} display         The Display name.
 * @return {String}                 The recently created Template name
 */
ContentType.prototype._getTemplateDisplayName = function (key, templateWrapper, display) {
  check(key, String);
  check(templateWrapper, String);
  check(display, String);

  var self = this;
  var copyOf  = templateWrapper+'_'+display;
  var copyTo  = templateWrapper+'_'+display+'_'+self._ctid;

  // Verify that origin template exists but if not we provide
  // default display as fallback.
  if(!Match.test(Template[copyOf], Blaze.Template)){
    copyOf = templateWrapper+'_default';
  }

  check(Template[copyOf].renderFunction, Function);

  // Duplicate the default template to make it Content Type specific.
  Template[copyTo] = new Template('Template.' + copyTo, Template[copyOf].renderFunction);

  return copyTo;
}

/**
 * Store the out-of-the-box Index + CRUD endpoint esqueleton. The object
 * returned here will be used to build the Router routes.
 *
 * @return {Object} The basic information needed to build the routes.
 */
ContentType.prototype._getEndPoints = function () {
  var self = this;

  var endpoints = {
    index: {
      enabled: true,
      display: 'default',
      path: self._basePath+'/'+self._ctid+'/index',
      name: 'ct.'+self._ctid+'.index',
      displays: {
        default: {}
      }
    },
    create: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/create',
      name: 'ct.'+this._ctid+'.create',
      displays: {
        default: {}
      }
    },
    read: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/:_id',
      name: 'ct.'+this._ctid+'.read',
      displays: {
        default: {}
      }
    },
    update: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/:_id/edit',
      name: 'ct.'+this._ctid+'.update',
      displays: {
        default: {}
      }
    },
    delete: {
      enabled: true,
      display: 'default',
      path: this._basePath+'/'+this._ctid+'/:_id/delete',
      name: 'ct.'+this._ctid+'.delete',
      displays: {
        default: {}
      }
    }
  }

  // @todo: check if this is still usefull.
  _.each(self.options.endpoints, function (endpoint, key) {
    if (Match.test(endpoint.enabled, Boolean)){
      endpoints[key].enabled = endpoint.enabled;
    }
  });

  return endpoints;
}

/**
 * Provide default helpers for Content Type templates.
 *
 * @param  {String} key The endpoint identifier.
 * @return {Object}     Meteor template helpers.
 */
ContentType.prototype._getTemplateHelpers = function (key) {
  check(key, String);

  var self = this;

  var helpers = {
    index: {
      meta: {
        title: 'Documents of type ' + self._ctid + ''
      },
      items: function () {
        var cursor = self._collection.find({});
        return {
          cursor: cursor,
          total: cursor.count()
        };
      }
    },
    create: {
      meta: {
        title: 'Create new ' + this._ctid + ''
      },
      formCollection: self._collection,
      formId: 'insert-form-'+self._ctid,
      formType: 'insert',
    },
    read: {
      meta: {
        title: 'View ' + this._ctid + ''
      },
      item: function () {
        var router = self.currentRoute();
        return self._collection.findOne({_id:router.params._id});
      }
    },
    update: {
      meta: {
        title: 'Update ' + this._ctid + ''
      },
      formCollection: self._collection,
      formId: 'update-form-'+self._ctid,
      formType: 'update',
      item: function () {
        var router = self.currentRoute();
        return self._collection.findOne({_id:router.params._id});
      }
    },
    delete: {
      meta: {
        title: 'Delete ' + this._ctid + ''
      },
      formCollection: self._collection,
      item: function () {
        var router = self.currentRoute();
        return self._collection.findOne({_id:router.params._id});
      }
    }
  };

  // Helpers common to all templates.
  helpers[key].ct = {
    fields: self._getSimpleSchemaFields(),
    labels: self._getTemplateLabels(),
    pathTo: {
      index:    'ct.'+self._ctid+'.index',
      create:   'ct.'+self._ctid+'.create',
      read:     'ct.'+self._ctid+'.read',
      update:   'ct.'+self._ctid+'.update',
      delete:   'ct.'+self._ctid+'.delete'
    }
  };

  return helpers[key];
}

/**
 * Provide default events for Content Type templates.
 *
 * @param  {String} key The endpoint identifier.
 * @return {Object}     Meteor template eventMap.
 */
ContentType.prototype._getTemplateEvents = function (key) {
  check(key, String);

  var self = this;

  var events = {
    index: {},
    create: {},
    read: {},
    update: {},
    delete: {}
  };

  return events[key];
}

/**
 * Label abstraction layer to let this package to be i18n friendly and to
 * provide custom labels in case you are using custom displays.
 *
 * @return {Object} Key-Value strings.
 */
ContentType.prototype._getTemplateLabels = function (labels) {
  var self = this;

  var labels = {
    backToIndex: "Back to Index",
    backToDocument: "Back to Document",
    confirmOk: "Yes, I'am sure",
    deletePrefix: "You are about to delete the document",
    deleteSuffix: "This action is unrecoverable. Are you sure?",
    linkView: "View",
    linkEdit: "Edit",
    linkDelete: "Delete",
    linkCreate: "Create new document",
    totalItemsPrefix: "Found",
    totalItemsSuffix: "item/s.",
    noItemsFound: "No documents found.",
    thKey: "Key",
    thLabel: "Label",
    thValue: "Value"
  }

  _.each(self.options.labels, function (label, key) {
    if (Match.test(label, String)){
      labels[key] = label;
    }
  });

  return labels;
}

/**
 * Builds an array with field keys and labels.
 *
 * @return {Object} An array with key-value objects.
 */
ContentType.prototype._getSimpleSchemaFields = function () {
  var self = this;

  return _.map(self._collection._c2._simpleSchema._schema, function(value, key){
    return {
      key: key,
      value: value.label
    };
  });
}

/**
 * Updates the display for specific endpoint.
 *
 * @param {String} key     The endpoint key.
 * @param {String} display The display name.
 */
ContentType.prototype.setDisplay = function (key, display) {
  check(key, String);
  check(display, String);
  check(this.displays[key], ReactiveVar);

  this.displays[key].set(display);
}

/**
 * Getter returning the current route with router abstraction.
 *
 * @return {Object} The current router route.
 */
ContentType.prototype.currentRoute = function () {
  var current = false;

  switch (ContentTypes.settings.router) {
    case 'iron_router':
      current = Router.current();
      break;
    case 'flow_router':
      current = Router.current();
      break;
  }
  return current;
}
<|endoftext|>


<commit_before>     easyRTC.setStreamAcceptor( function(callerEasyrtcid, stream) {
        var video = document.getElementById('caller');
        easyRTC.setVideoObjectSrc(video, stream);
    });

     easyRTC.setOnStreamClosed( function (callerEasyrtcid) {
        easyRTC.setVideoObjectSrc(document.getElementById('caller'), "");
    });

    function my_init() {
        easyRTC.setLoggedInListener( loggedInListener);
        var connectSuccess = function(myId) {
            console.log("My easyrtcid is " + myId);
        }
        var connectFailure = function(errmesg) {
            console.log(errmesg);
        }
              // easyRTC.enableAudio(false);

        easyRTC.initMediaSource(
              function(){      // success callback
                  var selfVideo = document.getElementById("self");
                  easyRTC.setVideoObjectSrc(selfVideo, easyRTC.getLocalStream());
                  easyRTC.connect("Company Chat Line", connectSuccess, connectFailure);
              },
              connectFailure
        );
     }

    function loggedInListener(connected) {
        var otherClientDiv = document.getElementById('otherClients');
        while (otherClientDiv.hasChildNodes()) {
            otherClientDiv.removeChild(otherClientDiv.lastChild);
        }
        for(var i in connected) {
            var button = document.createElement('button');
            button.onclick = function(easyrtcid) {
                return function() {
                    performCall(easyrtcid);
                }
            }(i);

            label = document.createTextNode(i);
            button.appendChild(label);
            otherClientDiv.appendChild(button);
        }
    }

    function performCall(easyrtcid) {
        easyRTC.call(
           easyrtcid,
           function(easyrtcid) { console.log("completed call to " + easyrtcid);},
           function(errorMessage) { console.log("err:" + errorMessage);},
           function(accepted, bywho) {
              console.log((accepted?"accepted":"rejected")+ " by " + bywho);
           }
       );
    }<commit_msg>Added audio<commit_after>     easyRTC.setStreamAcceptor( function(callerEasyrtcid, stream) {
        var video = document.getElementById('caller');
        easyRTC.setVideoObjectSrc(video, stream);
    });

     easyRTC.setOnStreamClosed( function (callerEasyrtcid) {
        easyRTC.setVideoObjectSrc(document.getElementById('caller'), "");
    });

    function my_init() {
        easyRTC.setLoggedInListener( loggedInListener);
        var connectSuccess = function(myId) {
            console.log("My easyrtcid is " + myId);
        }
        var connectFailure = function(errmesg) {
            console.log(errmesg);
        }
              easyRTC.enableAudio(true);

        easyRTC.initMediaSource(
              function(){      // success callback
                  var selfVideo = document.getElementById("self");
                  easyRTC.setVideoObjectSrc(selfVideo, easyRTC.getLocalStream());
                  easyRTC.connect("Company Chat Line", connectSuccess, connectFailure);
              },
              connectFailure
        );
     }

    function loggedInListener(connected) {
        var otherClientDiv = document.getElementById('otherClients');
        while (otherClientDiv.hasChildNodes()) {
            otherClientDiv.removeChild(otherClientDiv.lastChild);
        }
        for(var i in connected) {
            var button = document.createElement('button');
            button.onclick = function(easyrtcid) {
                return function() {
                    performCall(easyrtcid);
                }
            }(i);

            label = document.createTextNode(i);
            button.appendChild(label);
            otherClientDiv.appendChild(button);
        }
    }

    function performCall(easyrtcid) {
        easyRTC.call(
           easyrtcid,
           function(easyrtcid) { console.log("completed call to " + easyrtcid);},
           function(errorMessage) { console.log("err:" + errorMessage);},
           function(accepted, bywho) {
              console.log((accepted?"accepted":"rejected")+ " by " + bywho);
           }
       );
    }<|endoftext|>


<commit_before>import React from 'react';
import invariant from 'invariant';
import getRouteParams from './getRouteParams';

var { array, func, object } = React.PropTypes;

/**
 * A <RoutingContext> renders the component tree for a given router state
 * and sets the history object and the current location in context.
 */
var RoutingContext = React.createClass({

  propTypes: {
    history: object.isRequired,
    createElement: func.isRequired,
    location: object.isRequired,
    routes: array.isRequired,
    params: object.isRequired,
    components: array.isRequired
  },

  getDefaultProps() {
    return {
      createElement: React.createElement
    };
  },

  childContextTypes: {
    history: object.isRequired,
    location: object.isRequired
  },

  getChildContext() {
    return {
      history: this.props.history,
      location: this.props.location
    };
  },

  createElement(component, props) {
    return component == null ? null : this.props.createElement(component, props);
  },

  render() {
    var { history, location, routes, params, components } = this.props;
    var element = null;

    if (components) {
      element = components.reduceRight((element, components, index) => {
        if (components == null)
          return element; // Don't create new children; use the grandchildren.

        var route = routes[index];
        var routeParams = getRouteParams(route, params);
        var props = {
          history,
          location,
          params,
          route,
          routeParams
        };

        if (element)
          props.children = element;

        if (typeof components === 'object') {
          var elements = {};

          for (var key in components)
            if (components.hasOwnProperty(key))
              elements[key] = this.createElement(components[key], props);

          return elements;
        }

        return this.createElement(components, props);
      }, element);
    }

    invariant(
      element === null || element === false || React.isValidElement(element),
      'The root route must render a single element'
    );

    return element;
  }

});

export default RoutingContext;
<commit_msg>put `routes` back into route component props<commit_after>import React from 'react';
import invariant from 'invariant';
import getRouteParams from './getRouteParams';

var { array, func, object } = React.PropTypes;

/**
 * A <RoutingContext> renders the component tree for a given router state
 * and sets the history object and the current location in context.
 */
var RoutingContext = React.createClass({

  propTypes: {
    history: object.isRequired,
    createElement: func.isRequired,
    location: object.isRequired,
    routes: array.isRequired,
    params: object.isRequired,
    components: array.isRequired
  },

  getDefaultProps() {
    return {
      createElement: React.createElement
    };
  },

  childContextTypes: {
    history: object.isRequired,
    location: object.isRequired
  },

  getChildContext() {
    return {
      history: this.props.history,
      location: this.props.location
    };
  },

  createElement(component, props) {
    return component == null ? null : this.props.createElement(component, props);
  },

  render() {
    var { history, location, routes, params, components } = this.props;
    var element = null;

    if (components) {
      element = components.reduceRight((element, components, index) => {
        if (components == null)
          return element; // Don't create new children; use the grandchildren.

        var route = routes[index];
        var routeParams = getRouteParams(route, params);
        var props = {
          history,
          location,
          params,
          route,
          routeParams,
          routes
        };

        if (element)
          props.children = element;

        if (typeof components === 'object') {
          var elements = {};

          for (var key in components)
            if (components.hasOwnProperty(key))
              elements[key] = this.createElement(components[key], props);

          return elements;
        }

        return this.createElement(components, props);
      }, element);
    }

    invariant(
      element === null || element === false || React.isValidElement(element),
      'The root route must render a single element'
    );

    return element;
  }

});

export default RoutingContext;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Helmet from 'react-helmet';
import { StyleSheet, css } from 'aphrodite';
import FlatButton from 'material-ui/FlatButton';
import { red700, red300 } from 'material-ui/styles/colors';

const styles = StyleSheet.create({
  container: {
    margin: 'auto',
    width: '75%',
    padding: '10%',
  },
  header: {
    fontSize: '40px',
  },
  divider: {
    lineHeight: '110px',
    fontSize: '36px',
  },
  exploreButton: {
    marginLeft: '30px',
    color: 'white',
  },
  listItem: {
    listStylePosition: 'inside',
    fontSize: '36px',
    color: 'gray',
    lineHeight: '100px',
  },
  helpText: {
    fontSize: '36px',
    paddingLeft: '20px',
    color: 'gray',
  },
});

class ExploreSetup extends Component {

  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      progress: 0,
    };
    this.handleExploreComplete = this.handleExploreComplete.bind(this);
    this.handleExploreError = this.handleExploreError.bind(this);
    this.handleExploreProgress = this.handleExploreProgress.bind(this);
    this.startExplore = this.startExplore.bind(this);
  }

  componentDidMount() {
    document.addEventListener('explore_progress', this.handleExploreProgress);
    document.addEventListener('explore_done', this.handleExploreComplete);
    document.addEventListener('explore_error', this.handleExploreError);
  }

  componentWillUnmount() {
    document.removeEventListener('explore_progress', this.handleExploreProgress);
    document.removeEventListener('explore_done', this.handleExploreComplete);
    document.removeEventListener('explore_error', this.handleExploreError);
  }

  handleExploreComplete() {
    this.setState({ isLoading: false });
    console.log('explore finished!');
    this.props.onExploreComplete();
  }

  handleExploreError() {
    this.setState({ isLoading: false });
    console.log('explore errored!');
    this.props.onExploreError('error');
  }

  handleExploreProgress(progressEvent) {
    console.log(progressEvent.detail);
    this.setState({ progress: null });
  }

  startExplore() {
    this.setState({ isLoading: true });
    const event = document.createEvent('Event');
    event.initEvent('init_explore');
    document.dispatchEvent(event);
  }

  render() {
    return (

        <Helmet title="Explore" />
        { !this.state.isLoading &&

              To use this feature:

 }
        { this.state.isLoading ?

Finding articles for you...

This may take a minute

 :

                 Download Chrome Extension  and enable it!

                Login to Facebook on chrome!

              <FlatButton
                className={css(styles.exploreButton)}
                label="Get Started!"
                primary
                hoverColor={red300}
                backgroundColor={red700}
                onClick={this.startExplore}
              />

 }

    );
  }
}

ExploreSetup.propTypes = {
  onExploreComplete: PropTypes.func.isRequired,
  onExploreError: PropTypes.func.isRequired,
};

export default ExploreSetup;
<commit_msg>added circular progress indicator<commit_after>import React, { Component, PropTypes } from 'react';
import Helmet from 'react-helmet';
import { StyleSheet, css } from 'aphrodite';
import CircularProgress from 'material-ui/CircularProgress';
import FlatButton from 'material-ui/FlatButton';
import { red700, red300 } from 'material-ui/styles/colors';

const styles = StyleSheet.create({
  container: {
    margin: 'auto',
    width: '75%',
    padding: '10%',
  },
  header: {
    fontSize: '40px',
  },
  divider: {
    lineHeight: '110px',
    fontSize: '36px',
  },
  exploreButton: {
    marginLeft: '30px',
    color: 'white',
  },
  progressContainer: {
    marginTop: '25px',
  },
  listItem: {
    listStylePosition: 'inside',
    fontSize: '36px',
    color: 'gray',
    lineHeight: '100px',
  },
  helpText: {
    fontSize: '36px',
    paddingLeft: '20px',
    color: 'gray',
  },
});

class ExploreSetup extends Component {

  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      progress: [0, 282],
    };
    this.handleExploreComplete = this.handleExploreComplete.bind(this);
    this.handleExploreError = this.handleExploreError.bind(this);
    this.handleExploreProgress = this.handleExploreProgress.bind(this);
    this.startExplore = this.startExplore.bind(this);
  }

  componentDidMount() {
    document.addEventListener('explore_progress', this.handleExploreProgress);
    document.addEventListener('explore_done', this.handleExploreComplete);
    document.addEventListener('explore_error', this.handleExploreError);
  }

  componentWillUnmount() {
    document.removeEventListener('explore_progress', this.handleExploreProgress);
    document.removeEventListener('explore_done', this.handleExploreComplete);
    document.removeEventListener('explore_error', this.handleExploreError);
  }

  handleExploreComplete() {
    this.setState({ isLoading: false });
    console.log('explore finished!');
    this.props.onExploreComplete();
  }

  handleExploreError() {
    this.setState({ isLoading: false });
    console.log('explore errored!');
    this.props.onExploreError('error');
  }

  handleExploreProgress(progressEvent) {
    const { progress } = progressEvent.detail;
    this.setState({ progress });
  }

  startExplore() {
    this.setState({ isLoading: true });
    const event = document.createEvent('Event');
    event.initEvent('init_explore');
    document.dispatchEvent(event);
  }

  render() {
    return (

        <Helmet title="Explore" />
        { !this.state.isLoading &&

              To use this feature:

 }
        { this.state.isLoading ?

Finding articles for you...

              <CircularProgress
                mode="determinate"
                value={this.state.progress[0]}
                size={150}
                color={red700}
                thickness={10}
                max={this.state.progress[1]}
              />

 :

                 Download the Notist chrome extension

                Login to Facebook on chrome

              <FlatButton
                className={css(styles.exploreButton)}
                label="Get Started!"
                primary
                hoverColor={red300}
                backgroundColor={red700}
                onClick={this.startExplore}
              />

 }

    );
  }
}

ExploreSetup.propTypes = {
  onExploreComplete: PropTypes.func.isRequired,
  onExploreError: PropTypes.func.isRequired,
};

export default ExploreSetup;
<|endoftext|>


<commit_before>const highlightjs = require('highlight.js')
const marked = require('marked')
const remote = require('remote')
const ipc = require('ipc')
const conf = remote.getGlobal('conf')

marked.setOptions({
  highlight: function (code, lang) {
    return highlightjs.highlightAuto(code, [ lang ]).value
  }
})

ipc.on('md', function (raw) {
  const md = marked(raw)
  const base = document.querySelector('base')
  const body = document.querySelector('.markdown-body')
  base.setAttribute('href', remote.getGlobal('baseUrl'))
  body.innerHTML = md
})

window.addEventListener('keydown', function (ev) {
  var esc = ev.keyCode === 27
  var w = ev.keyCode === 87
  var ctrlW = ev.ctrlKey && w
  var cmdW = ev.metaKey && w

  if (esc || ctrlW || cmdW) remote.getCurrentWindow().close()
})

var zoom = require('./zoom')(conf.zoom)

// menu
var vmdSubmenu = [
  { label: 'Quit', accelerator: 'CmdOrCtrl+Q', click: function () { remote.require('app').quit() } }
]

if (process.platform === 'darwin') {
  vmdSubmenu = [
    { label: 'About vmd', selector: 'orderFrontStandardAboutPanel:' },
    { type: 'separator' }
  ].concat(vmdSubmenu)
}

var template = [
  {
    label: 'vmd',
    submenu: vmdSubmenu
  },
  {
    label: 'File',
    submenu: [
      { label: 'Print', accelerator: 'CmdOrCtrl+P', click: function () { window.print() } }
    ]
  },
  {
    label: 'Edit',
    submenu: [
      { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
      { label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' }
    ]
  },
  {
    label: 'View',
    submenu: [
      { label: 'Zoom In', accelerator: 'CmdOrCtrl+Plus', click: function () { zoom.zoomIn() } },
      { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-', click: function () { zoom.zoomOut() } },
      { label: 'Reset Zoom', accelerator: 'CmdOrCtrl+0', click: function () { zoom.reset() } }
    ]
  }
]

var Menu = remote.require('menu')
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
<commit_msg>replace selector with callback<commit_after>const highlightjs = require('highlight.js')
const marked = require('marked')
const remote = require('remote')
const ipc = require('ipc')
const conf = remote.getGlobal('conf')

marked.setOptions({
  highlight: function (code, lang) {
    return highlightjs.highlightAuto(code, [ lang ]).value
  }
})

ipc.on('md', function (raw) {
  const md = marked(raw)
  const base = document.querySelector('base')
  const body = document.querySelector('.markdown-body')
  base.setAttribute('href', remote.getGlobal('baseUrl'))
  body.innerHTML = md
})

window.addEventListener('keydown', function (ev) {
  var esc = ev.keyCode === 27
  var w = ev.keyCode === 87
  var ctrlW = ev.ctrlKey && w
  var cmdW = ev.metaKey && w

  if (esc || ctrlW || cmdW) remote.getCurrentWindow().close()
})

var zoom = require('./zoom')(conf.zoom)

// menu
var vmdSubmenu = [
  { label: 'Quit', accelerator: 'CmdOrCtrl+Q', click: function () { remote.require('app').quit() } }
]

if (process.platform === 'darwin') {
  vmdSubmenu = [
    { label: 'About vmd', selector: 'orderFrontStandardAboutPanel:' },
    { type: 'separator' }
  ].concat(vmdSubmenu)
}

var template = [
  {
    label: 'vmd',
    submenu: vmdSubmenu
  },
  {
    label: 'File',
    submenu: [
      { label: 'Print', accelerator: 'CmdOrCtrl+P', click: function () { window.print() } }
    ]
  },
  {
    label: 'Edit',
    submenu: [
        { label: 'Copy', accelerator: 'CmdOrCtrl+C', click: function() { document.execCommand("copy") } },
        { label: 'Select All', accelerator: 'CmdOrCtrl+A', click: function() { document.execCommand("selectAll") } }
    ]
  },
  {
    label: 'View',
    submenu: [
      { label: 'Zoom In', accelerator: 'CmdOrCtrl+Plus', click: function () { zoom.zoomIn() } },
      { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-', click: function () { zoom.zoomOut() } },
      { label: 'Reset Zoom', accelerator: 'CmdOrCtrl+0', click: function () { zoom.reset() } }
    ]
  }
]

var Menu = remote.require('menu')
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
<|endoftext|>


<commit_before>var _               = require('underscore');
var combineReducers = require('redux').combineReducers;
var pluralize       = require('pluralize');

var resourceReducer = function(resource) {
	var RESOURCE = resource.toUpperCase();

	return combineReducers({
		updating: function(state, action) {
			switch (action.type) {
				case 'UPDATING_' + RESOURCE:
					return true;
				case 'UPDATED_' + RESOURCE:
					return false;
				default:
					return state || false;
			}
		},
		removing: function(state, action) {
			switch (action.type) {
				case 'REMOVING_' + RESOURCE:
					return true;
				case 'REMOVED_' + RESOURCE:
					return false;
				default:
					return state || false;
			}
		},
		error: function(state, action) {
			switch (action.type) {
				case 'UPDATING_' + RESOURCE:
				case 'REMOVING_' + RESOURCE:
					return null;
				case 'UPDATED_' + RESOURCE:
				case 'REMOVED_' + RESOURCE:
					return action.error ? action.payload : null;
				default:
					return state || null;
			}
		},
		data: function(state, action) {
			switch (action.type) {
				case 'CREATED_' + RESOURCE:
				case 'UPDATED_' + RESOURCE:
					return action.error ? state : action.payload;
				case 'REMOVED_' + RESOURCE:
					return action.error ? state : null;
				default:
					return state || {};
			}
		}
	});
};

var resourcesReducer = function(resource) {
	var RESOURCE  = resource.toUpperCase();
	var RESOURCES = pluralize(resource).toUpperCase();

	var singleItemReducer = resourceReducer(resource, {});

	return function(state, action) {
		switch (action.type) {
			case 'LOADED_' + RESOURCES:
				return action.error ? state : _.chain(action.payload)
					.indexBy('id')
					.mapObject(function(object) {
						return { data: object };
					})
					.defaults(state)
					.value();
			case 'REMOVED_' + RESOURCE:
				state = _.clone(state);
				if (action.error) {
					state[action.payload.id] = singleItemReducer(state[action.payload.id], action);
				} else {
					delete state[action.payload.id];
				}
				return state || {};
			default:
				if (!_.result(action.payload, 'id')) {
					return state || {};
				}
				state = _.clone(state);
				state[action.payload.id] = singleItemReducer(state[action.payload.id], action);
				return state || {};
		}
	};
};

module.exports = resourcesReducer;
<commit_msg>Wasn't handling id=0 properly<commit_after>var _               = require('underscore');
var combineReducers = require('redux').combineReducers;
var pluralize       = require('pluralize');

var resourceReducer = function(resource) {
	var RESOURCE = resource.toUpperCase();

	return combineReducers({
		updating: function(state, action) {
			switch (action.type) {
				case 'UPDATING_' + RESOURCE:
					return true;
				case 'UPDATED_' + RESOURCE:
					return false;
				default:
					return state || false;
			}
		},
		removing: function(state, action) {
			switch (action.type) {
				case 'REMOVING_' + RESOURCE:
					return true;
				case 'REMOVED_' + RESOURCE:
					return false;
				default:
					return state || false;
			}
		},
		error: function(state, action) {
			switch (action.type) {
				case 'UPDATING_' + RESOURCE:
				case 'REMOVING_' + RESOURCE:
					return null;
				case 'UPDATED_' + RESOURCE:
				case 'REMOVED_' + RESOURCE:
					return action.error ? action.payload : null;
				default:
					return state || null;
			}
		},
		data: function(state, action) {
			switch (action.type) {
				case 'CREATED_' + RESOURCE:
				case 'UPDATED_' + RESOURCE:
					return action.error ? state : action.payload;
				case 'REMOVED_' + RESOURCE:
					return action.error ? state : null;
				default:
					return state || {};
			}
		}
	});
};

var resourcesReducer = function(resource) {
	var RESOURCE  = resource.toUpperCase();
	var RESOURCES = pluralize(resource).toUpperCase();

	var singleItemReducer = resourceReducer(resource, {});

	return function(state, action) {
		switch (action.type) {
			case 'LOADED_' + RESOURCES:
				return action.error ? state : _.chain(action.payload)
					.indexBy('id')
					.mapObject(function(object) {
						return { data: object };
					})
					.defaults(state)
					.value();
			case 'REMOVED_' + RESOURCE:
				state = _.clone(state);
				if (action.error) {
					state[action.payload.id] = singleItemReducer(state[action.payload.id], action);
				} else {
					delete state[action.payload.id];
				}
				return state || {};
			default:
				if (_.chain(action.payload).result('id').isUndefined().value()) {
					return state || {};
				}
				state = _.clone(state);
				state[action.payload.id] = singleItemReducer(state[action.payload.id], action);
				return state || {};
		}
	};
};

module.exports = resourcesReducer;
<|endoftext|>


<commit_before>(function($) {
    $(function() {
        $.aip.wall = (function() {
            var inc = function(name, value) {
                var $t = $('#loading li[name="' + name + '"]');
                if (value == undefined) value = 1;
                $t.text(parseInt($t.text()) + value);
            };
            var options = {
                makePageData: $.noop,
                pulling_threshold: {{ config['AIP_PULLING_THRESHOLD'] }}
            };
            var $container = $('#items');
            var page = 0;
            function progress(p) {
                $('#bar').width(p + '%');
            };
            var marsed = false;
            var mars = function($item) {
                $container.append($item);
                if (!marsed) {
                    console.log('initialize masonry');
                    $container.masonry({
                        itemSelector: '.item',
                        isAnimated: true,
                        columnWidth: '.span2',
                        transitionDuration: '0.4s'
                    });
                    marsed = true;
                } else {
                    $container.masonry('appended', $item, true);
                }
            };
            var $buf = $('#buffer');
            var nomore = false;
            var pulling = false;
            var rest = function() {
                return $buf.find('.item').length;
            };
            var enough = function() {
                return rest() >= options.pulling_threshold;
            };
            var pull = function() {
                if (nomore) return;
                if (pulling) return;
                if (enough()) {
                    $.aip.notice('enough (' + rest() + '), pulling cancel');
                    return;
                } else {
                    $.aip.notice('not enough (' + rest() + '), pulling start');
                }
                pulling = true;
                $this = $(this);
                progress(0);
                $('#loading').show();
                $buf.empty();
                var data = options.makePageData(page);
                if ($.aip.user_id()) {
                    data.user_id = $.aip.user_id();
                }
                $.get(
                    options.makePageUrl(page),
                    data
                ).done(function(data) {
                    pulling = false;
                    var $items = $(data).find('.item');
                    var n = $items.length;
                    var cleanup = function() {
                        $('#loading').hide();
                        $('#alert_box').html('');
                        page += 1;
                        if (n) {
                            $.waypoints('refresh');
                            if ($container.outerHeight() <= $.waypoints('viewportHeight')) pull();
                        }
                    };
                    if (!n) {
                        cleanup();
                        nomore = true;
                        return;
                    }
                    inc('in-json', n);
                    $items.each(function() {
                        $(this).attr('data-done', false);
                    });
                    $buf.append($items);
                    var loaded = 0;
                    // no exception
                    var doneone = function($item) {
                        ++loaded;
                        progress(100 * loaded / n);
                        if (loaded == n) {
                            cleanup();
                        } else if (loaded > n) {
                            throw 'loaded(' + loaded + ') > n(' + n + ')';
                        }
                    };
                    var guarded_doneone = function($item) {
                        if ($item.attr('data-done') == 'false') {
                            doneone($item);
                            $item.attr('data-done', true);
                        }
                    };
                    var dealone = function($item) {
                        if ($item.data('dealed')) return;
                        try {
                            $.aip.init_plus($item);
                            $.aip.init_tags($item);
                            $.aip.init_detail($item);
                            mars($item);
                            inc('done');
                        } catch (e) {
                            console.log('dealone failed');
                            console.log(e);
                            console.trace();
                        } finally {
                            $item.data('dealed', true);
                            guarded_doneone($item);
                        }
                    };
                    var thumbnail = function($item) {
                        var error = function(message) {
                            $.aip.notice(message);
                            guarded_doneone($item);
                        };
                        $.aip.thumbnail({
                            '$item': $item
                        }).done(function() {
                            inc('proxied');
                            $item.find('.loading').hide();
                            $item.find('img.preview').show();
                            dealone($item);
                        }).fail(function(reason) {
                            error('load image failed, reason: ' + JSON.stringify(reason));
                            inc('proxied-preview-loading-failed');
                        });
                    };
                    $items.each(function() {
                        var $this = $(this);
                        var $img = $this.find('img.preview');
                        var done = function() {
                            console.log($img.attr('src') + 'loaded');
                            $this.find('.loading').hide();
                            $img.show();
                            $.aip.super_resolution($img, function() {
                                thumbnail($this);
                            }, function() {
                                dealone($this);
                            });
                        };
                        var fail = function(reason) {
                            console.log('load image failed, reason: ' + reason);
                            inc('original-preview-loading-failed');
                            thumbnail($this);
                        };
                        $.aip.load_image({
                            img: $img,
                            src: $img.data('src'),
                            timeout: 1e3 * {{ config['AIP_LOADING_TIMEOUT'] }},
                            reloads: $.aip.range({{ config['AIP_RELOAD_LIMIT'] }}).map(function() {
                                return $.aip.disturb({{ config['AIP_RELOAD_INTERVAL'] }});
                            })
                        }).done(done).fail(fail);
                    });
                }).fail(function(reason) {
                    $('#loading').hide();
                    $.aip.notice('load more failed, reason: ' + JSON.stringify(reason));
                });
            };
            return {
                init: function(kargs) {
                    console.log('init wall');
                    options = $.extend(options, kargs);
                    pull();
                    $('.level-wall').on('resize scrollstop', function() {
                        if ($('.bottom-anchor').visible(true)) pull();
                    });
                }
            };
        })();
    });
})(jQuery);
<commit_msg>bottom progress bar removed, use notice instead<commit_after>(function($) {
    $(function() {
        $.aip.wall = (function() {
            var inc = function(name, value) {
                var $t = $('#loading li[name="' + name + '"]');
                if (value == undefined) value = 1;
                $t.text(parseInt($t.text()) + value);
            };
            var options = {
                makePageData: $.noop,
                pulling_threshold: {{ config['AIP_PULLING_THRESHOLD'] }}
            };
            var $container = $('#items');
            var page = 0;
            function progress(p) {
                $('#bar').width(p + '%');
            };
            var marsed = false;
            var mars = function($item) {
                $container.append($item);
                if (!marsed) {
                    console.log('initialize masonry');
                    $container.masonry({
                        itemSelector: '.item',
                        isAnimated: true,
                        columnWidth: '.span2',
                        transitionDuration: '0.4s'
                    });
                    marsed = true;
                } else {
                    $container.masonry('appended', $item, true);
                }
            };
            var $buf = $('#buffer');
            var nomore = false;
            var pulling = false;
            var rest = function() {
                return $buf.find('.item').length;
            };
            var enough = function() {
                return rest() >= options.pulling_threshold;
            };
            var pull = function() {
                if (nomore) return;
                if (pulling) return;
                if (enough()) {
                    console.log('enough (' + rest() + '), pulling cancel');
                    return;
                } else {
                    $.aip.notice('not enough (' + rest() + '), pulling start');
                }
                pulling = true;
                $this = $(this);
                progress(0);
                $buf.empty();
                var data = options.makePageData(page);
                if ($.aip.user_id()) {
                    data.user_id = $.aip.user_id();
                }
                $.get(
                    options.makePageUrl(page),
                    data
                ).done(function(data) {
                    pulling = false;
                    var $items = $(data).find('.item');
                    var n = $items.length;
                    var cleanup = function() {
                        $('#alert_box').html('');
                        page += 1;
                        if (n) {
                            $.waypoints('refresh');
                            if ($container.outerHeight() <= $.waypoints('viewportHeight')) pull();
                        }
                    };
                    if (!n) {
                        cleanup();
                        nomore = true;
                        return;
                    }
                    inc('in-json', n);
                    $items.each(function() {
                        $(this).attr('data-done', false);
                    });
                    $buf.append($items);
                    var loaded = 0;
                    // no exception
                    var doneone = function($item) {
                        ++loaded;
                        progress(100 * loaded / n);
                        if (loaded == n) {
                            cleanup();
                        } else if (loaded > n) {
                            throw 'loaded(' + loaded + ') > n(' + n + ')';
                        }
                    };
                    var guarded_doneone = function($item) {
                        if ($item.attr('data-done') == 'false') {
                            doneone($item);
                            $item.attr('data-done', true);
                        }
                    };
                    var dealone = function($item) {
                        if ($item.data('dealed')) return;
                        try {
                            $.aip.init_plus($item);
                            $.aip.init_tags($item);
                            $.aip.init_detail($item);
                            mars($item);
                            inc('done');
                        } catch (e) {
                            console.log('dealone failed');
                            console.log(e);
                            console.trace();
                        } finally {
                            $item.data('dealed', true);
                            guarded_doneone($item);
                        }
                    };
                    var thumbnail = function($item) {
                        var error = function(message) {
                            $.aip.notice(message);
                            guarded_doneone($item);
                        };
                        $.aip.thumbnail({
                            '$item': $item
                        }).done(function() {
                            inc('proxied');
                            $item.find('.loading').hide();
                            $item.find('img.preview').show();
                            dealone($item);
                        }).fail(function(reason) {
                            error('load image failed, reason: ' + JSON.stringify(reason));
                            inc('proxied-preview-loading-failed');
                        });
                    };
                    $items.each(function() {
                        var $this = $(this);
                        var $img = $this.find('img.preview');
                        var done = function() {
                            console.log($img.attr('src') + 'loaded');
                            $this.find('.loading').hide();
                            $img.show();
                            $.aip.super_resolution($img, function() {
                                thumbnail($this);
                            }, function() {
                                dealone($this);
                            });
                        };
                        var fail = function(reason) {
                            console.log('load image failed, reason: ' + reason);
                            inc('original-preview-loading-failed');
                            thumbnail($this);
                        };
                        $.aip.load_image({
                            img: $img,
                            src: $img.data('src'),
                            timeout: 1e3 * {{ config['AIP_LOADING_TIMEOUT'] }},
                            reloads: $.aip.range({{ config['AIP_RELOAD_LIMIT'] }}).map(function() {
                                return $.aip.disturb({{ config['AIP_RELOAD_INTERVAL'] }});
                            })
                        }).done(done).fail(fail);
                    });
                }).fail(function(reason) {
                    $.aip.notice('load more failed, reason: ' + JSON.stringify(reason));
                });
            };
            return {
                init: function(kargs) {
                    console.log('init wall');
                    options = $.extend(options, kargs);
                    pull();
                    $('.level-wall').on('resize scrollstop', function() {
                        if ($('.bottom-anchor').visible(true)) pull();
                    });
                }
            };
        })();
    });
})(jQuery);
<|endoftext|>


<commit_before>import * as internal from "../../src/base/internal.js";
import * as dom from "../../src/core/dom.js";
import * as template from "../../src/core/template.js";
import PlainListBox from "../../src/plain/PlainListBox.js";

class DataListBox extends PlainListBox {
  get data() {
    return this[internal.state].data;
  }
  set data(data) {
    this[internal.setState]({ data });
  }

  get dataItemAdapter() {
    return this[internal.state].dataItemAdapter;
  }
  set dataItemAdapter(dataItemAdapter) {
    this[internal.setState]({ dataItemAdapter });
  }

  get [internal.defaultState]() {
    return Object.assign(super[internal.defaultState], {
      data: null,
      dataItemAdapter: defaultDataItemAdapter,
      itemPartType: "div",
      items: null,
    });
  }

  [internal.render](changed) {
    super[internal.render](changed);

    if (changed.items) {
      const slot = this[internal.ids].slot;
      if (slot) {
        const items = this[internal.state].items || [];
        dom.updateChildNodes(slot, items);
      }
    }
  }

  [internal.stateEffects](state, changed) {
    const effects = super[internal.stateEffects]
      ? super[internal.stateEffects](state, changed)
      : {};

    if (changed.data || changed.dataItemAdapter || changed.itemPartType) {
      const { data, dataItemAdapter, itemPartType } = state;
      const items =
        data == null
          ? null
          : data.map((entry) => {
              const element = template.createElement(itemPartType);
              dataItemAdapter(entry, element);
              return element;
            });
      Object.assign(effects, { items });
    }

    return effects;
  }

  get [internal.template]() {
    const result = super[internal.template];

    const defaultSlot = result.content.querySelector("slot:not([name])");
    if (defaultSlot) {
      defaultSlot.id = "slot";
      defaultSlot.name = uuidv4();
    }

    return result;
  }
}

function defaultDataItemAdapter(data, item) {
  item.textContent = data.toString();
}

// Generate a UUID
// https://stackoverflow.com/a/2117523/76472
function uuidv4() {
  return `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, (c) =>
    (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16)
  );
}

export default DataListBox;
customElements.define("data-list-box", DataListBox);
<commit_msg>Try sealing slot by replacing it with a div styled with display: contents.<commit_after>import * as internal from "../../src/base/internal.js";
import * as dom from "../../src/core/dom.js";
import html from "../../src/core/html.js";
import * as template from "../../src/core/template.js";
import PlainListBox from "../../src/plain/PlainListBox.js";

class DataListBox extends PlainListBox {
  get data() {
    return this[internal.state].data;
  }
  set data(data) {
    this[internal.setState]({ data });
  }

  get dataItemAdapter() {
    return this[internal.state].dataItemAdapter;
  }
  set dataItemAdapter(dataItemAdapter) {
    this[internal.setState]({ dataItemAdapter });
  }

  get [internal.defaultState]() {
    return Object.assign(super[internal.defaultState], {
      data: null,
      dataItemAdapter: defaultDataItemAdapter,
      itemPartType: "div",
      items: null,
    });
  }

  [internal.render](changed) {
    super[internal.render](changed);

    if (changed.items) {
      const slot = this[internal.ids].slot;
      if (slot) {
        const items = this[internal.state].items || [];
        dom.updateChildNodes(slot, items);
      }
    }
  }

  [internal.stateEffects](state, changed) {
    const effects = super[internal.stateEffects]
      ? super[internal.stateEffects](state, changed)
      : {};

    if (changed.data || changed.dataItemAdapter || changed.itemPartType) {
      const { data, dataItemAdapter, itemPartType } = state;
      const items =
        data == null
          ? null
          : data.map((entry) => {
              const element = template.createElement(itemPartType);
              dataItemAdapter(entry, element);
              return element;
            });
      Object.assign(effects, { items });
    }

    return effects;
  }

  get [internal.template]() {
    const result = super[internal.template];

    const defaultSlot = result.content.querySelector("slot:not([name])");
    if (defaultSlot) {
      defaultSlot.replaceWith(html`<div id="slot"></div>`);
    }

    result.content.append(html`
      <style>
        #slot {
          display: contents;
        }
      </style>
    `);

    return result;
  }
}

function defaultDataItemAdapter(data, item) {
  item.textContent = data.toString();
}

export default DataListBox;
customElements.define("data-list-box", DataListBox);
<|endoftext|>


<commit_before>
/**
 * @constructor
 */

var Connection = function () {
    this.sockjs_path = '/socket';
    this.authenticated = false;

    this._sockjs = null;
    this._callbacks = {};
    this._pointer = 1;
    this._events = {};
    this._loggingContext = console ? console : {};
};

/**
 * Start the Connection and add the listeners
 *
 * @param {SockJS} SockJS |  Optional
 * @param {object} options | Required
 */

Connection.prototype.start = function (SockJS, options) {
    if(typeof SockJS !== "function") {
        options = SockJS;
    }

    // Setup basic options
    options                 = options                || {};
    options.url             = options.url            || window.location.hostname;
    options.sockjs_path     = options.sockjs_path    || this.sockjs_path;
    options.logging         = options.logging        || function(){};
    options.loggingContext  = options.loggingContext || this._loggingContext; // Fix for logging in chrome

    // Check for valid logging function
    if( 'function' !== typeof options.logging) {
        options.logging = function(){}
    }

    // Check if SockJS is loaded
    if( 'function' !== typeof SockJS ) {
        options.logging.call(options.loggingContext, 'Connection :: error :: SockJS is undefined');
        this.emit('failure');
        return;
    }

    // Check for URL and port
    if(!options.hasOwnProperty('port') || !options.hasOwnProperty('url')) {
        options.logging.call(options.loggingContext, 'Connection :: error :: URL or Port not defined');
        return;
    }

    // Add http:// || https:// if its not already there
    if( 'http' != options.url.substr(0, 4) ) {
        var protocol = window.location.protocol + '//' || 'http://';
        options.url = protocol + options.url;
    }

    // Start socket connection with SockJS
    options.logging.call(options.loggingContext, 'Connection :: Starting socket interface on: ' + options.url + ':' + options.port + options.sockjs_path);
    this._sockjs = new SockJS(options.url + ':' + options.port + options.sockjs_path);
    var self = this;

    // Handle connection open event
    this._sockjs.onopen = function () {
        self.emit('connect');
    };

    // Handle connection closed event
    this._sockjs.onclose = function () {
        self.emit('close');
    };

    // Handle message events
    this._sockjs.onmessage = function (raw_message) {
        // Parse message
        try {
            var message = JSON.parse(raw_message.data);
        } catch (error) {
            options.logging.call(options.loggingContext, "Connection :: Error parsing message: ", error);
            return;
        }

        // Check for type
        if (!message.hasOwnProperty('type')) {
            options.logging.call(options.loggingContext, "Connection :: Invalid message: no type specified :: ", message);
            return;
        }

        // Check for bundles
        if('bundle' == message.type) {
            for( var i = 0; i < message.data.length; i ++ ) {
                var bundleItem = message.data[i];
                if(!bundleItem.hasOwnProperty('data')) {
                    options.logging.call(options.loggingContext, "Connection :: Bundle Invalid message: no data specified :: ", bundleItem);
                    return;
                }

                if(bundleItem.hasOwnProperty('callback_id')) {
                    self.executeCallback(bundleItem.callback_id, bundleItem.data);
                }

                if(bundleItem.hasOwnProperty('type')) {
                    self.emit(bundleItem.type, bundleItem.data);
                }
            }
        }
        else {
            // Check for callback
            if(message.hasOwnProperty('callback_id')) {
                self.executeCallback( callback_id, message.data );
            }

            // Emit the message
            self.emit( message.type, message.data );
        }
    };

};

/**
 * Execute a given callback_id
 *
 * @param callback_id
 * @param data
 */
Connection.prototype.executeCallback = function(callback_id, data) {
    if(this._callbacks.hasOwnProperty(callback_id)) {
        this._callbacks[callback_id].call(null, data);
    }
};

/**
 * Send an authentication request to the server with a token.
 *
 * @param {any} token
 */

Connection.prototype.authenticate = function (token) {
    var self = this;
    this.send('authenticate', {token: token}, function (data) {
        if( data.result ) {
            self.authenticated = true;
        }
        self.emit('authenticated', data);
    });
};

/**
 * Send data to the server
 *
 * @param {string} type
 * @param {object} data
 * @param {function} callback
 */

Connection.prototype.send = function (type, data, callback) {
    var _data = {type: type, data: data};

    if (typeof(callback) === 'function') {
        this._callbacks[this._pointer] = callback;
        _data.callback_id = this._pointer;
        this._pointer += 1;
    }

    this._sockjs.send(JSON.stringify(_data));
};

/**
 * Disconnect the connection
 */
Connection.prototype.disconnect = function() {
    if(this._sockjs._transport && this._sockjs._transport.ws)
        this._sockjs._transport.ws.onclose();
};

/**
 * Event system
 * @param {string} event
 * @param {function} cb
 */

Connection.prototype.on = function(event, cb) {
    if( ! this._events.hasOwnProperty(event) ) {
        this._events[event] = [];
    }
    this._events[event].push(cb);
};

/**
 * Event system
 * @param event
 */
Connection.prototype.emit = function(event) {
    var args = Array.prototype.slice.call(arguments).slice(1);

    if( this._events.hasOwnProperty(event) ) {
        var events = this._events[event];
        for ( var key in events ) {
            if(events.hasOwnProperty(key)) {
                events[key].apply(null, args);
            }
        }
    }
};

/**
 * Export to the window or as module export based on environment
 */
(function (factory){
    if(typeof exports === 'object') {
        module.exports = exports = factory;
    } else {
        window.Connection = factory;
    }
})(Connection);
<commit_msg>minor bugfix & allow no data to be send<commit_after>
/**
 * @constructor
 */

var Connection = function () {
    this.sockjs_path = '/socket';
    this.authenticated = false;

    this._sockjs = null;
    this._callbacks = {};
    this._pointer = 1;
    this._events = {};
    this._loggingContext = console ? console : {};
};

/**
 * Start the Connection and add the listeners
 *
 * @param {SockJS} SockJS |  Optional
 * @param {object} options | Required
 */

Connection.prototype.start = function (SockJS, options) {
    if(typeof SockJS !== "function") {
        options = SockJS;
    }

    // Setup basic options
    options                 = options                || {};
    options.url             = options.url            || window.location.hostname;
    options.sockjs_path     = options.sockjs_path    || this.sockjs_path;
    options.logging         = options.logging        || function(){};
    options.loggingContext  = options.loggingContext || this._loggingContext; // Fix for logging in chrome

    // Check for valid logging function
    if( 'function' !== typeof options.logging) {
        options.logging = function(){}
    }

    // Check if SockJS is loaded
    if( 'function' !== typeof SockJS ) {
        options.logging.call(options.loggingContext, 'Connection :: error :: SockJS is undefined');
        this.emit('failure');
        return;
    }

    // Check for URL and port
    if(!options.hasOwnProperty('port') || !options.hasOwnProperty('url')) {
        options.logging.call(options.loggingContext, 'Connection :: error :: URL or Port not defined');
        return;
    }

    // Add http:// || https:// if its not already there
    if( 'http' != options.url.substr(0, 4) ) {
        var protocol = window.location.protocol + '//' || 'http://';
        options.url = protocol + options.url;
    }

    // Start socket connection with SockJS
    options.logging.call(options.loggingContext, 'Connection :: Starting socket interface on: ' + options.url + ':' + options.port + options.sockjs_path);
    this._sockjs = new SockJS(options.url + ':' + options.port + options.sockjs_path);
    var self = this;

    // Handle connection open event
    this._sockjs.onopen = function () {
        self.emit('connect');
    };

    // Handle connection closed event
    this._sockjs.onclose = function () {
        self.emit('close');
    };

    // Handle message events
    this._sockjs.onmessage = function (raw_message) {
        // Parse message
        try {
            var message = JSON.parse(raw_message.data);
        } catch (error) {
            options.logging.call(options.loggingContext, "Connection :: Error parsing message: ", error);
            return;
        }

        // Check for type
        if (!message.hasOwnProperty('type')) {
            options.logging.call(options.loggingContext, "Connection :: Invalid message: no type specified :: ", message);
            return;
        }

        // Check for bundles
        if('bundle' == message.type) {
            for( var i = 0; i < message.data.length; i ++ ) {
                var bundleItem = message.data[i];
                if(!bundleItem.hasOwnProperty('data')) {
                    bundleItem.data = {};
                }

                if(bundleItem.hasOwnProperty('callback_id')) {
                    self.executeCallback(bundleItem.callback_id, bundleItem.data);
                }

                if(bundleItem.hasOwnProperty('type')) {
                    self.emit(bundleItem.type, bundleItem.data);
                }
            }
        }
        else {
            // Check for callback
            if(message.hasOwnProperty('callback_id')) {
                self.executeCallback(message.callback_id, message.data);
            }

            // Emit the message
            self.emit(message.type, message.data);
        }
    };

};

/**
 * Execute a given callback_id
 *
 * @param callback_id
 * @param data
 */
Connection.prototype.executeCallback = function(callback_id, data) {
    if(this._callbacks.hasOwnProperty(callback_id)) {
        this._callbacks[callback_id].call(null, data);
    }
};

/**
 * Send an authentication request to the server with a token.
 *
 * @param {any} token
 */

Connection.prototype.authenticate = function (token) {
    var self = this;
    this.send('authenticate', {token: token}, function (data) {
        if( data.result ) {
            self.authenticated = true;
        }
        self.emit('authenticated', data);
    });
};

/**
 * Send data to the server
 *
 * @param {string} type
 * @param {object} data
 * @param {function} callback
 */

Connection.prototype.send = function (type, data, callback) {
    var _data = {type: type, data: data};

    if (typeof(callback) === 'function') {
        this._callbacks[this._pointer] = callback;
        _data.callback_id = this._pointer;
        this._pointer += 1;
    }

    this._sockjs.send(JSON.stringify(_data));
};

/**
 * Disconnect the connection
 */
Connection.prototype.disconnect = function() {
    if(this._sockjs._transport && this._sockjs._transport.ws)
        this._sockjs._transport.ws.onclose();
};

/**
 * Event system
 * @param {string} event
 * @param {function} cb
 */

Connection.prototype.on = function(event, cb) {
    if( ! this._events.hasOwnProperty(event) ) {
        this._events[event] = [];
    }
    this._events[event].push(cb);
};

/**
 * Event system
 * @param event
 */
Connection.prototype.emit = function(event) {
    var args = Array.prototype.slice.call(arguments).slice(1);

    if( this._events.hasOwnProperty(event) ) {
        var events = this._events[event];
        for ( var key in events ) {
            if(events.hasOwnProperty(key)) {
                events[key].apply(null, args);
            }
        }
    }
};

/**
 * Export to the window or as module export based on environment
 */
(function (factory){
    if(typeof exports === 'object') {
        module.exports = exports = factory;
    } else {
        window.Connection = factory;
    }
})(Connection);
<|endoftext|>


<commit_before>export default {
  dropdownOpened: {
    data: false,
    about: false,
    myrw: false,
    topics: false
  },
  mobileOpened: false,
  searchOpened: false,
  items: [
    {
      id: 'data',
      label: 'Data',
      route: 'data',
      pathnames: ['/app/Explore', '/app/ExploreDetail', '/app/Pulse'],
      children: [
        { label: 'Explore Datasets', route: 'explore' },
        { label: 'Dashboards', route: 'dashboards' },
        { label: 'Planet Pulse', href: '/data/pulse' },
        { label: 'App Gallery', route: 'get_involved_detail', params: { id: 'apps' } }
      ]
    },
    {
      id: 'topics',
      label: 'Topics',
      route: 'topics',
      pathnames: ['/app/topic-detail'],
      children: [
        { label: 'Biodiversity', route: 'topics_detail', params: { id: 'biodiversity' } },
        { label: 'Cities', route: 'topics_detail', params: { id: 'cities' } },
        { label: 'Climate', route: 'topics_detail', params: { id: 'climate' } },
        { label: 'Commerce', route: 'topics_detail', params: { id: 'commerce' } },
        { label: 'Energy', route: 'topics_detail', params: { id: 'energy' } },
        { label: 'Food and Agriculture', route: 'topics_detail', params: { id: 'food-and-agriculture' } },
        { label: 'Forests', route: 'topics_detail', params: { id: 'forest' } },
        { label: 'Water', route: 'topics_detail', params: { id: 'water' } }
      ]
    },
    {
      id: 'blog',
      label: 'Blog',
      route: 'insights',
      pathnames: ['/app/Insights']
    },
    {
      id: 'about',
      label: 'About',
      route: 'about',
      pathnames: ['/app/About', '/app/Partners'],
      children: [
        { label: 'Partners', route: 'about_partners' },
        { label: 'FAQs', route: 'about_faqs' },
        { label: 'How to', route: 'about_howto' },
        { label: 'Contact us', route: 'about_contact-us' }
      ]
    },
    {
      id: 'get_involved',
      label: 'Get Involved',
      route: 'get_involved',
      pathnames: ['/app/GetInvolved']
    },
    {
      id: 'search',
      label: 'Search'
    },
    {
      id: 'myrw',
      label: 'Personal Area',
      route: 'myrw'
    }
  ]
};
<commit_msg>Topics: remove dashboards from menu<commit_after>export default {
  dropdownOpened: {
    data: false,
    about: false,
    myrw: false,
    topics: false
  },
  mobileOpened: false,
  searchOpened: false,
  items: [
    {
      id: 'data',
      label: 'Data',
      route: 'data',
      pathnames: ['/app/Explore', '/app/ExploreDetail', '/app/Pulse'],
      children: [
        { label: 'Explore Datasets', route: 'explore' },
        { label: 'Planet Pulse', href: '/data/pulse' },
        { label: 'App Gallery', route: 'get_involved_detail', params: { id: 'apps' } }
      ]
    },
    {
      id: 'topics',
      label: 'Topics',
      route: 'topics',
      pathnames: ['/app/topic-detail'],
      children: [
        { label: 'Biodiversity', route: 'topics_detail', params: { id: 'biodiversity' } },
        { label: 'Cities', route: 'topics_detail', params: { id: 'cities' } },
        { label: 'Climate', route: 'topics_detail', params: { id: 'climate' } },
        { label: 'Commerce', route: 'topics_detail', params: { id: 'commerce' } },
        { label: 'Energy', route: 'topics_detail', params: { id: 'energy' } },
        { label: 'Food and Agriculture', route: 'topics_detail', params: { id: 'food-and-agriculture' } },
        { label: 'Forests', route: 'topics_detail', params: { id: 'forest' } },
        { label: 'Water', route: 'topics_detail', params: { id: 'water' } }
      ]
    },
    {
      id: 'blog',
      label: 'Blog',
      route: 'insights',
      pathnames: ['/app/Insights']
    },
    {
      id: 'about',
      label: 'About',
      route: 'about',
      pathnames: ['/app/About', '/app/Partners'],
      children: [
        { label: 'Partners', route: 'about_partners' },
        { label: 'FAQs', route: 'about_faqs' },
        { label: 'How to', route: 'about_howto' },
        { label: 'Contact us', route: 'about_contact-us' }
      ]
    },
    {
      id: 'get_involved',
      label: 'Get Involved',
      route: 'get_involved',
      pathnames: ['/app/GetInvolved']
    },
    {
      id: 'search',
      label: 'Search'
    },
    {
      id: 'myrw',
      label: 'Personal Area',
      route: 'myrw'
    }
  ]
};
<|endoftext|>