<commit_before>'use strict';

/* global _, $, moment, PNotify */
var CartUtility = function() {
    this.spinTasksCount = 0;
    this.pnotifyStack = {'dir1': 'up', 'dir2': 'right', 'push': 'bottom', 'spacing1': 0, 'spacing2': 0};
};

CartUtility.prototype.post = function($http, $q, url, data, onSuccess, onError) {
    var self = this;
    var deferred = $q.defer();

    var options = {
        method: "POST",
        url: url,
        headers: {"Content-type": "application/x-www-form-urlencoded"}
    };
    if (_.isObject(data) && !_.isEmpty(data)) {
        options.data = $.param(data);
    }

    $http(options)
    .success(function(data) {
        if (self.handleResponse(data)) {
            deferred.resolve(onSuccess(data));
        } else {
            deferred.reject();
        }
    })
    .error(function(data) {
        if (_.isFunction(onError)) {
            onError(data);
        }
        deferred.reject();
    });

    return self.spinShow(deferred.promise);
};

CartUtility.prototype.spinShow = function(promise) {
    var self = this;
    self.spinTasksCount++;
    $('.spinner-mask').show();
    if (_.isObject(promise) && promise.hasOwnProperty('finally')) {
        // this is a promise
        promise['finally'](function() {
            self.spinHide();
        });
    } else {
        setTimeout(function() {
            self.spinHide();
        }, 300);
    }
    return promise;
};

CartUtility.prototype.spinHide = function() {
    this.spinTasksCount--;
    if (this.spinTasksCount > 0) {
        return; // still spin tasks left
    }
    $('.spinner-mask').hide();
};

CartUtility.prototype.notify = function(title, text, type, duration) {
    // type: "notice", "info", "success", or "error"
    // duration: milliseconds
    if (_.isUndefined(title)) {
        title = 'title';
    }
    if (_.isUndefined(text)) {
        text = '';
    }
    if (_.isUndefined(type) || _.isEmpty(type) || _.isNull(type)) {
        type = 'info';
    }
    if (_.isUndefined(duration) || _.isEmpty(duration) || _.isNull(duration)) {
        duration = 3000; // 3s
    }

    var options = {
        title: title,
        text: text,
        type: type,
        delay: duration,
        addclass: 'stack-bar-bottom',
        width: '70%',
        stack: this.pnotifyStack
    };
    if (type === 'error') {
        options.hide = false;
    }

    new PNotify(options);
};

CartUtility.prototype.handleResponse = function(result, notifySuccess, notifyError) {
    if (!_.isBoolean(notifySuccess)) {
        notifySuccess = false; // default not display the succeed message
    }
    if (!_.isBoolean(notifyError)) {
        notifyError = true; // default display the failure message
    }

    if (_.isObject(result) && result.hasOwnProperty('valid') && result.valid === false) {
        // request failed with normal framework exception
        if (notifyError) { // show failure msg box
            this.notify('REQ Failed!', result.message.error, 'error');
        }
        return false;
    } else if (!_.isObject(result) || (_.isObject(result) && !result.hasOwnProperty('valid'))) {
        // request failed without normal framework exception
        if (notifyError) { // show failure msg box
            this.notify('REQ Failed!');
        }
        return false;
    } else {
        // request done
        if (notifySuccess) { // show success msg box, and hide automatically
            this.notify('REQ Succeed!', JSON.stringify(result.message));
        }
        return true;
    }
};

CartUtility.prototype.buildToc = function(html, baseUrl) {
    if (_.isUndefined(baseUrl) || _.isNull(baseUrl)) {
        baseUrl = ''; // shall be 'http://...'
    }
    var rootNodeName = 'root';
    /**
     * [
     *     { // index of this node is 5
     *         "level": 3,
     *         "text": "headerTextForLv3",
     *         "anchor": "CartUtility.escapeAnchorName(headerTextForLv3)"
     *         "parent": 4, // index of node in this array, only root parent is string 'root'
     *         "html": $('text')
     *     },
     *     ...
     * ]
     */
    var headers = [];

    if (_.isUndefined(html) || _.isEmpty(html) || _.isNull(html)) {
        return '';
    }

    // find all headers, and collect basic info
    var headerInfos = $(html).filter(':header');
    if (headerInfos.length <= 0) {
        return ''; // no headers found
    }
    _.forEach(headerInfos, function(header) {
        // build basic headers info
        var hElement = $(header);
        var headerLevel = parseInt(hElement.prop("tagName").substring(1));
        var headerText = hElement.text();
        var anchorName = hElement.find('a').attr('name');
        headers.push({
            "level": headerLevel,
            "text": headerText,
            "anchor": anchorName,
            "parent": null,
            "html": null
        });
    });
    var headersCloned = _.clone(headers);
    // loop to build html code & find parent levels
    _.forEach(headers, function(header, index) {
        header.html = $('
' + header.text + '
');
        if (index === 0) {
            header.parent = rootNodeName;
        } else {
            var reversedBeforeHeaders = headersCloned.slice(0, index).reverse();
            _.forEach(reversedBeforeHeaders, function(bfHeader, bfIndex) {
                if (header.parent !== null) {
                    // means parent already determined, skip this loop
                    return;
                }
                if (bfHeader.level === header.level) {
                    // same level, shall share the same parent node
                    header.parent = bfHeader.parent;
                } else if (bfHeader.level  header.level) {
                    // current level is lower than target, dismiss it
                }
            });
            if (header.parent === null) {
                // parent not found, root node
                header.parent = rootNodeName;
            }
        }
    });

    // build final html codes
    var root = $('

');
    // build non root nodes first, and have to reverse headers first
    // since we need to handle sub headers from the bottom to top
    _.forEach(headers, function(header) {
        if (header.parent === rootNodeName) {
            return; // dismiss root nodes, since the content in it has not been built yet
        }
        var parentNode = headers[header.parent].html;
        if (parentNode.find('ul').length <= 0) {
            // parent li element does not contain the ul element, add it
            parentNode.append('

');
        }
        $(parentNode.find('ul')[0]).append(header.html);
    });
    // build root nodes
    _.forEach(headers, function(header) {
        if (header.parent === rootNodeName) {
            root.append(header.html);
        }
    });

    return '
' + root.html() + '
'; // since html() only returned the innerHtml of the element
};

CartUtility.prototype.toggleToc = function(icon, toc) { /* all params are jquery elements: $('#id') */
    if (toc.html() !== '') {
        toc.slideToggle('fast', function() {
            icon.toggleClass('glyphicon-plus').toggleClass('glyphicon-minus');
        });
    }
};

CartUtility.prototype.escapeAnchorName = function(anchorName) {
    return encodeURIComponent(anchorName);
};

CartUtility.prototype.getPureRootUrlFromLocation = function($location) {
    return $location.protocol() + '://' + $location.host() + '/';
};

CartUtility.prototype.getPureAbsUrlFromLocation = function($location) {
    return $location.protocol() + '://' + $location.host() + '/' + $location.path();
};

CartUtility.prototype.log = function(msg, facility) {
    if (_.isUndefined(msg) || _.isEmpty(msg) || _.isNull(msg)) {
        return;
    }
    if (_.isObject(console)) {
        if (_.isObject(msg)) {
            msg = JSON.stringify(msg);
        }
        msg = '[' + moment().format('YYYY-MM-DD HH:mm:ss') + ']' + ((_.isString(facility)) ? (' ' + facility) : '') + ' ' + msg;
        console.log(msg);
    }
};

CartUtility.prototype.isDndSupported = function() {
    var div = document.createElement('div');
    return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
};

CartUtility.prototype.generateMdHTMLHeader = function(post) {
    return '

' + JSON.stringify({
        "uuid": post.uuid,
        "title": post.title,
        "category": post.category,
        "tags": post.tags,
        "attachments": post.attachments
    }) + '

';
};

CartUtility.prototype.parseMdHTMLHeader = function(markdown) {
    return JSON.parse($(markdown).find('.mdHeader').text());
};

CartUtility.prototype.parseMdPureContent = function(markdown) {
    return markdown.substr(markdown.indexOf('

') + '

'.length);
};

CartUtility.prototype.focusEditor = function(editor) {
    editor.focus(); // focus the ace editor
    editor.resize(true); // resize the window

    var editorValues = editor.getSession().getValue();
    if (_.isEmpty(editorValues)) {
        return; // no content, no need to redirect to end of page
    }

    editorValues = editorValues.split("\n");
    var lineNo = editorValues.length; // count total number of lines
    var columnNo = editorValues[lineNo - 1].split('').length;
    editor.gotoLine(lineNo, columnNo); // go to end of document
};

module.exports = new CartUtility();<commit_msg>Add time utility functions.<commit_after>'use strict';

/* global _, $, moment, PNotify */
var CartUtility = function() {
    this.spinTasksCount = 0;
    this.pnotifyStack = {'dir1': 'up', 'dir2': 'right', 'push': 'bottom', 'spacing1': 0, 'spacing2': 0};
};

CartUtility.prototype.post = function($http, $q, url, data, onSuccess, onError) {
    var self = this;
    var deferred = $q.defer();

    var options = {
        method: "POST",
        url: url,
        headers: {"Content-type": "application/x-www-form-urlencoded"}
    };
    if (_.isObject(data) && !_.isEmpty(data)) {
        options.data = $.param(data);
    }

    $http(options)
    .success(function(data) {
        if (self.handleResponse(data)) {
            deferred.resolve(onSuccess(data));
        } else {
            deferred.reject();
        }
    })
    .error(function(data) {
        if (_.isFunction(onError)) {
            onError(data);
        }
        deferred.reject();
    });

    return self.spinShow(deferred.promise);
};

CartUtility.prototype.spinShow = function(promise) {
    var self = this;
    self.spinTasksCount++;
    $('.spinner-mask').show();
    if (_.isObject(promise) && promise.hasOwnProperty('finally')) {
        // this is a promise
        promise['finally'](function() {
            self.spinHide();
        });
    } else {
        setTimeout(function() {
            self.spinHide();
        }, 300);
    }
    return promise;
};

CartUtility.prototype.spinHide = function() {
    this.spinTasksCount--;
    if (this.spinTasksCount > 0) {
        return; // still spin tasks left
    }
    $('.spinner-mask').hide();
};

CartUtility.prototype.notify = function(title, text, type, duration) {
    // type: "notice", "info", "success", or "error"
    // duration: milliseconds
    if (_.isUndefined(title)) {
        title = 'title';
    }
    if (_.isUndefined(text)) {
        text = '';
    }
    if (_.isUndefined(type) || _.isEmpty(type) || _.isNull(type)) {
        type = 'info';
    }
    if (_.isUndefined(duration) || _.isEmpty(duration) || _.isNull(duration)) {
        duration = 3000; // 3s
    }

    var options = {
        title: title,
        text: text,
        type: type,
        delay: duration,
        addclass: 'stack-bar-bottom',
        width: '70%',
        stack: this.pnotifyStack
    };
    if (type === 'error') {
        options.hide = false;
    }

    new PNotify(options);
};

CartUtility.prototype.handleResponse = function(result, notifySuccess, notifyError) {
    if (!_.isBoolean(notifySuccess)) {
        notifySuccess = false; // default not display the succeed message
    }
    if (!_.isBoolean(notifyError)) {
        notifyError = true; // default display the failure message
    }

    if (_.isObject(result) && result.hasOwnProperty('valid') && result.valid === false) {
        // request failed with normal framework exception
        if (notifyError) { // show failure msg box
            this.notify('REQ Failed!', result.message.error, 'error');
        }
        return false;
    } else if (!_.isObject(result) || (_.isObject(result) && !result.hasOwnProperty('valid'))) {
        // request failed without normal framework exception
        if (notifyError) { // show failure msg box
            this.notify('REQ Failed!');
        }
        return false;
    } else {
        // request done
        if (notifySuccess) { // show success msg box, and hide automatically
            this.notify('REQ Succeed!', JSON.stringify(result.message));
        }
        return true;
    }
};

CartUtility.prototype.buildToc = function(html, baseUrl) {
    if (_.isUndefined(baseUrl) || _.isNull(baseUrl)) {
        baseUrl = ''; // shall be 'http://...'
    }
    var rootNodeName = 'root';
    /**
     * [
     *     { // index of this node is 5
     *         "level": 3,
     *         "text": "headerTextForLv3",
     *         "anchor": "CartUtility.escapeAnchorName(headerTextForLv3)"
     *         "parent": 4, // index of node in this array, only root parent is string 'root'
     *         "html": $('text')
     *     },
     *     ...
     * ]
     */
    var headers = [];

    if (_.isUndefined(html) || _.isEmpty(html) || _.isNull(html)) {
        return '';
    }

    // find all headers, and collect basic info
    var headerInfos = $(html).filter(':header');
    if (headerInfos.length <= 0) {
        return ''; // no headers found
    }
    _.forEach(headerInfos, function(header) {
        // build basic headers info
        var hElement = $(header);
        var headerLevel = parseInt(hElement.prop("tagName").substring(1));
        var headerText = hElement.text();
        var anchorName = hElement.find('a').attr('name');
        headers.push({
            "level": headerLevel,
            "text": headerText,
            "anchor": anchorName,
            "parent": null,
            "html": null
        });
    });
    var headersCloned = _.clone(headers);
    // loop to build html code & find parent levels
    _.forEach(headers, function(header, index) {
        header.html = $('
' + header.text + '
');
        if (index === 0) {
            header.parent = rootNodeName;
        } else {
            var reversedBeforeHeaders = headersCloned.slice(0, index).reverse();
            _.forEach(reversedBeforeHeaders, function(bfHeader, bfIndex) {
                if (header.parent !== null) {
                    // means parent already determined, skip this loop
                    return;
                }
                if (bfHeader.level === header.level) {
                    // same level, shall share the same parent node
                    header.parent = bfHeader.parent;
                } else if (bfHeader.level  header.level) {
                    // current level is lower than target, dismiss it
                }
            });
            if (header.parent === null) {
                // parent not found, root node
                header.parent = rootNodeName;
            }
        }
    });

    // build final html codes
    var root = $('

');
    // build non root nodes first, and have to reverse headers first
    // since we need to handle sub headers from the bottom to top
    _.forEach(headers, function(header) {
        if (header.parent === rootNodeName) {
            return; // dismiss root nodes, since the content in it has not been built yet
        }
        var parentNode = headers[header.parent].html;
        if (parentNode.find('ul').length <= 0) {
            // parent li element does not contain the ul element, add it
            parentNode.append('

');
        }
        $(parentNode.find('ul')[0]).append(header.html);
    });
    // build root nodes
    _.forEach(headers, function(header) {
        if (header.parent === rootNodeName) {
            root.append(header.html);
        }
    });

    return '
' + root.html() + '
'; // since html() only returned the innerHtml of the element
};

CartUtility.prototype.toggleToc = function(icon, toc) { /* all params are jquery elements: $('#id') */
    if (toc.html() !== '') {
        toc.slideToggle('fast', function() {
            icon.toggleClass('glyphicon-plus').toggleClass('glyphicon-minus');
        });
    }
};

CartUtility.prototype.escapeAnchorName = function(anchorName) {
    return encodeURIComponent(anchorName);
};

CartUtility.prototype.getPureRootUrlFromLocation = function($location) {
    return $location.protocol() + '://' + $location.host() + '/';
};

CartUtility.prototype.getPureAbsUrlFromLocation = function($location) {
    return $location.protocol() + '://' + $location.host() + '/' + $location.path();
};

CartUtility.prototype.log = function(msg, facility) {
    if (_.isUndefined(msg) || _.isEmpty(msg) || _.isNull(msg)) {
        return;
    }
    if (_.isObject(console)) {
        if (_.isObject(msg)) {
            msg = JSON.stringify(msg);
        }
        msg = '[' + moment().format('YYYY-MM-DD HH:mm:ss') + ']' + ((_.isString(facility)) ? (' ' + facility) : '') + ' ' + msg;
        console.log(msg);
    }
};

CartUtility.prototype.isDndSupported = function() {
    var div = document.createElement('div');
    return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
};

CartUtility.prototype.generateMdHTMLHeader = function(post) {
    return '

' + JSON.stringify({
        "uuid": post.uuid,
        "title": post.title,
        "category": post.category,
        "tags": post.tags,
        "attachments": post.attachments
    }) + '

';
};

CartUtility.prototype.parseMdHTMLHeader = function(markdown) {
    return JSON.parse($(markdown).find('.mdHeader').text());
};

CartUtility.prototype.parseMdPureContent = function(markdown) {
    return markdown.substr(markdown.indexOf('

') + '

'.length);
};

CartUtility.prototype.focusEditor = function(editor) {
    editor.focus(); // focus the ace editor
    editor.resize(true); // resize the window

    var editorValues = editor.getSession().getValue();
    if (_.isEmpty(editorValues)) {
        return; // no content, no need to redirect to end of page
    }

    editorValues = editorValues.split("\n");
    var lineNo = editorValues.length; // count total number of lines
    var columnNo = editorValues[lineNo - 1].split('').length;
    editor.gotoLine(lineNo, columnNo); // go to end of document
};

CartUtility.prototype.getTime = function() {
    return moment().unix(); // return current timestamp
};

CartUtility.prototype.getTimeString = function(timestamp) {
    var instance = _.isUndefined(timestamp) ? moment() : this.parseUnixTime(timestamp);
    return instance.format('YYYY-MM-DD HH:mm:ss');
};

CartUtility.prototype.parseUnixTime = function(timestamp) {
    return moment.unix(timestamp); // return moment instance
};

CartUtility.prototype.parseUnixYear = function(timestamp, needFull) {
    if (_.isUndefined(needFull) || !_.isBoolean(needFull)) {
        needFull = false;
    }
    // return 'YYYY' or 'YYYY-01-01 00:00:00'
    return this.parseUnixTime(timestamp).format('YYYY') + (needFull ? '-01-01 00:00:00' : '');
};

CartUtility.prototype.parseUnixYearEndTimestamp = function(timestamp) {
    // return 'NEXT_YYYY-01-01 00:00:00'.timestamp - 1
    return moment((parseInt(this.parseUnixYear(timestamp)) + 1) + '-01-01 00:00:00').unix() - 1;
};

CartUtility.prototype.parseUnixDate = function(timestamp, needFull) {
    if (_.isUndefined(needFull) || !_.isBoolean(needFull)) {
        needFull = false;
    }
    // return 'YYYY-MM' or 'YYYY-MM-01 00:00:00'
    return this.parseUnixTime(timestamp).format('YYYY-MM') + (needFull ? '-01 00:00:00' : '');
};

CartUtility.prototype.parseUnixMonth = function(timestamp) {
    // return 'MM'
    return this.parseUnixTime(timestamp).format('MM');
};

CartUtility.prototype.parseUnixDateEndTimestamp = function(timestamp) {
    var year = parseInt(this.parseUnixYear(timestamp));
    var month = parseInt(this.parseUnixMonth(timestamp));
    if (month === 12) {
        year++;
        month = '01';
    } else {
        month++;
    }
    if ((month + '').length === 1) {
        month = '0' + month;
    }
    // month is 12 => return 'NEXT_YYYY-01-01 00:00:00'.timestamp - 1
    // month lower than 12 => return 'YYYY-NEXT_MM-01 00:00:00'.timestamp - 1
    return moment(year + '-' + month + '-01 00:00:00').unix() - 1;
};

module.exports = new CartUtility();<|endoftext|>


<commit_before>function parseDatacolumn(DATACOLUMN) {
  var obj = {};

  for (var i = 0, ilen = DATACOLUMN.attributes.length; i < ilen; i++) {
    obj[DATACOLUMN.attributes[i].name] = DATACOLUMN.attributes[i].value || true;
  }

  obj.data = obj.value;
  delete obj.value;

  obj.readOnly = obj.readonly;
  delete obj.readonly;

  obj.checkedTemplate = obj.checkedtemplate;
  delete obj.checkedtemplate;

  obj.uncheckedTemplate = obj.uncheckedtemplate;
  delete obj.uncheckedtemplate;

  if (obj.type === 'autocomplete' && typeof obj.source === 'string') {
    obj.source = window[obj.source];
  }

  var HANDSONTABLE = DATACOLUMN.getElementsByTagName('x-handsontable');
  if (HANDSONTABLE.length) {
    obj.handsontable = parseHandsontable(HANDSONTABLE[0]);
  }

  return obj;
}

function getModel(HANDSONTABLE) {
  if(HANDSONTABLE.templateInstance) {
    return HANDSONTABLE.templateInstance.model;
  }
  else {
    return window;
  }
}

function getModelPath(HANDSONTABLE, path) {
  var obj = getModel(HANDSONTABLE);
  var keys = path.split('.');
  var len = keys.length;
  for (var i = 0; i < len; i++) {
    obj = obj[keys[i]];
  }
  return obj;
}

function parseHandsontable(HANDSONTABLE) {
  var columns = []
    , i
    , ilen;

  for (i = 0, ilen = HANDSONTABLE.childNodes.length; i < ilen; i++) {
    if (HANDSONTABLE.childNodes[i].nodeName === 'DATACOLUMN') {
      columns.push(parseDatacolumn(HANDSONTABLE.childNodes[i]));
    }
  }

  var options = {
    data: getModelPath(HANDSONTABLE, HANDSONTABLE.datarows),
    width: HANDSONTABLE.width,
    height: HANDSONTABLE.height,
    columns: columns,
    minSpareRows: HANDSONTABLE.minsparerows,
    colHeaders: readBool(HANDSONTABLE.colheaders),
    fillHandle: readBool(HANDSONTABLE.fillhandle),
    autoWrapRow: true,
    contextMenu: true,
    observeChanges: true,
    getValue: HANDSONTABLE.getvalue
  };

  function hasTitle(column) {
    return column.title !== void 0;
  }

  if (columns.filter(hasTitle).length && options.colHeaders !== false) {
    options.colHeaders = true;
  }

  if (HANDSONTABLE.settings) {
    var settings = getModelPath(HANDSONTABLE, HANDSONTABLE.settings);
    for (i in settings) {
      if (settings.hasOwnProperty(i)) {
        options[i] = settings[i];
      }
    }
  }

  return options;
}

var publicMethods = ['updateSettings', 'loadData', 'render', 'setDataAtCell', 'setDataAtRowProp', 'getDataAtCell', 'getDataAtRowProp', 'countRows', 'countCols', 'rowOffset', 'colOffset', 'countVisibleRows', 'countVisibleCols', 'clear', 'clearUndo', 'getData', 'alter', 'getCell', 'getCellMeta', 'selectCell', 'deselectCell', 'getSelected', 'destroyEditor', 'getRowHeader', 'getColHeader', 'destroy', 'isUndoAvailable', 'isRedoAvailable', 'undo', 'redo', 'countEmptyRows', 'countEmptyCols', 'isEmptyRow', 'isEmptyCol', 'parseSettingsFromDOM', 'addHook', 'addHookOnce', 'getValue'];

var publish = {};
for (var i = 0, ilen = publicMethods.length; i < ilen; i++) {
  publish[publicMethods[i]] = (function (methodName) {
    return function () {
      return this.instance[methodName].apply(this.instance, arguments);
    }
  })(publicMethods[i]);
}

function readBool(val) {
  if (val === void 0 || val === "false") {
    return false;
  }
  return val;
}

Polymer('x-handsontable', {
  instance: null,
  created: function () {
    jQuery(this.$.htContainer).handsontable(parseHandsontable(this));

    this.instance = jQuery(this.$.htContainer).data('handsontable');
  },
  publish: publish
});<commit_msg>only continue setting up a path if exists (is this needed in long term?)<commit_after>function parseDatacolumn(DATACOLUMN) {
  var obj = {};

  for (var i = 0, ilen = DATACOLUMN.attributes.length; i < ilen; i++) {
    obj[DATACOLUMN.attributes[i].name] = DATACOLUMN.attributes[i].value || true;
  }

  obj.data = obj.value;
  delete obj.value;

  obj.readOnly = obj.readonly;
  delete obj.readonly;

  obj.checkedTemplate = obj.checkedtemplate;
  delete obj.checkedtemplate;

  obj.uncheckedTemplate = obj.uncheckedtemplate;
  delete obj.uncheckedtemplate;

  if (obj.type === 'autocomplete' && typeof obj.source === 'string') {
    obj.source = window[obj.source];
  }

  var HANDSONTABLE = DATACOLUMN.getElementsByTagName('x-handsontable');
  if (HANDSONTABLE.length) {
    obj.handsontable = parseHandsontable(HANDSONTABLE[0]);
  }

  return obj;
}

function getModel(HANDSONTABLE) {
  if(HANDSONTABLE.templateInstance) {
    return HANDSONTABLE.templateInstance.model;
  }
  else {
    return window;
  }
}

function getModelPath(HANDSONTABLE, path) {
  var obj = getModel(HANDSONTABLE);
  var keys = path.split('.');
  var len = keys.length;
  for (var i = 0; i < len; i++) {
    if (obj[keys[i]]) {
      obj = obj[keys[i]];
    }
  }
  return obj;
}

function parseHandsontable(HANDSONTABLE) {
  var columns = []
    , i
    , ilen;

  for (i = 0, ilen = HANDSONTABLE.childNodes.length; i < ilen; i++) {
    if (HANDSONTABLE.childNodes[i].nodeName === 'DATACOLUMN') {
      columns.push(parseDatacolumn(HANDSONTABLE.childNodes[i]));
    }
  }

  var options = {
    data: getModelPath(HANDSONTABLE, HANDSONTABLE.datarows),
    width: HANDSONTABLE.width,
    height: HANDSONTABLE.height,
    columns: columns,
    minSpareRows: HANDSONTABLE.minsparerows,
    colHeaders: readBool(HANDSONTABLE.colheaders),
    fillHandle: readBool(HANDSONTABLE.fillhandle),
    autoWrapRow: true,
    contextMenu: true,
    observeChanges: true,
    getValue: HANDSONTABLE.getvalue
  };

  function hasTitle(column) {
    return column.title !== void 0;
  }

  if (columns.filter(hasTitle).length && options.colHeaders !== false) {
    options.colHeaders = true;
  }

  if (HANDSONTABLE.settings) {
    var settings = getModelPath(HANDSONTABLE, HANDSONTABLE.settings);
    for (i in settings) {
      if (settings.hasOwnProperty(i)) {
        options[i] = settings[i];
      }
    }
  }

  return options;
}

var publicMethods = ['updateSettings', 'loadData', 'render', 'setDataAtCell', 'setDataAtRowProp', 'getDataAtCell', 'getDataAtRowProp', 'countRows', 'countCols', 'rowOffset', 'colOffset', 'countVisibleRows', 'countVisibleCols', 'clear', 'clearUndo', 'getData', 'alter', 'getCell', 'getCellMeta', 'selectCell', 'deselectCell', 'getSelected', 'destroyEditor', 'getRowHeader', 'getColHeader', 'destroy', 'isUndoAvailable', 'isRedoAvailable', 'undo', 'redo', 'countEmptyRows', 'countEmptyCols', 'isEmptyRow', 'isEmptyCol', 'parseSettingsFromDOM', 'addHook', 'addHookOnce', 'getValue'];

var publish = {};
for (var i = 0, ilen = publicMethods.length; i < ilen; i++) {
  publish[publicMethods[i]] = (function (methodName) {
    return function () {
      return this.instance[methodName].apply(this.instance, arguments);
    }
  })(publicMethods[i]);
}

function readBool(val) {
  if (val === void 0 || val === "false") {
    return false;
  }
  return val;
}

Polymer('x-handsontable', {
  instance: null,
  created: function () {
    jQuery(this.$.htContainer).handsontable(parseHandsontable(this));

    this.instance = jQuery(this.$.htContainer).data('handsontable');
  },
  publish: publish
});<|endoftext|>


<commit_before>var _ = require("lodash");
var filterTools = require("../util/filterTools")();

module.exports = function () {

    var filter = {},
        nodes,
        properties,
        enabled = false,
        filteredNodes,
        filteredProperties,
        tags = [];

    /**
     * If enabled, all elements which have one of the predefined label are filtered.
     * @param untouchedNodes
     * @param untouchedProperties
     */
    filter.filter = function (untouchedNodes, untouchedProperties) {
        nodes = untouchedNodes;
        properties = untouchedProperties;

        if (this.enabled()) {
            removeTags();
        }

        filteredNodes = nodes;
        filteredProperties = properties;
    };

    function removeTags() {
        var filteredData = filterTools.filterNodesAndTidy(nodes, properties, hasNoTag);

        nodes = filteredData.nodes;
        properties = filteredData.properties;
    }

    function hasNoTag(node) {
        var nodeTags = node.tags();

        if(_.isEmpty(nodeTags)) return true;

        nodeTags = _.invokeMap(nodeTags, String.prototype.toLowerCase);

        return _.isEmpty(_.intersection(tags, nodeTags));

    }

    filter.enabled = function (p) {
        if (!arguments.length) return enabled;
        enabled = p;
        return filter;
    };

    filter.addTag = function(tag) {
        if(!tag) return;

        var tag = String.prototype.toLowerCase.apply(tag);

        if(_.indexOf(tags, tag) === -1) {
            tags.push(tag);
        }

    };

    filter.removeTag = function (tagToRemove) {
        if(!tagToRemove) return;

        var tagToRemove = String.prototype.toLowerCase.apply(tagToRemove);

        _.remove(tags, function (tag) {
            return tag ===  tagToRemove;
        });
    };

    filter.reset = function () {
      tags = [];
    };
    // Functions a filter must have
    filter.filteredNodes = function () {
        return filteredNodes;
    };

    filter.filteredProperties = function () {
        return filteredProperties;
    };

    return filter;
};
<commit_msg>Inverted the tag filtering<commit_after>var _ = require("lodash");
var filterTools = require("../util/filterTools")();

module.exports = function () {

    var filter = {},
        nodes,
        properties,
        enabled = false,
        filteredNodes,
        filteredProperties,
        tags = [];

    /**
     * If enabled, all elements which have one of the predefined label are filtered.
     * @param untouchedNodes
     * @param untouchedProperties
     */
    filter.filter = function (untouchedNodes, untouchedProperties) {
        nodes = untouchedNodes;
        properties = untouchedProperties;

        if (this.enabled()) {
            removeTags();
        }

        filteredNodes = nodes;
        filteredProperties = properties;
    };

    function removeTags() {
        var filteredData = filterTools.filterNodesAndTidy(nodes, properties, hasTag);

        nodes = filteredData.nodes;
        properties = filteredData.properties;
    }

    function hasTag(node) {
        var nodeTags = node.tags();

        if(_.isEmpty(nodeTags)) return false;

        nodeTags = _.invokeMap(nodeTags, String.prototype.toLowerCase);

        return !_.isEmpty(_.intersection(tags, nodeTags));

    }

    filter.enabled = function (p) {
        if (!arguments.length) return enabled;
        enabled = p;
        return filter;
    };

    filter.addTag = function(tag) {
        if(!tag) return;

        var tag = String.prototype.toLowerCase.apply(tag);

        if(_.indexOf(tags, tag) === -1) {
            tags.push(tag);
        }

    };

    filter.removeTag = function (tagToRemove) {
        if(!tagToRemove) return;

        var tagToRemove = String.prototype.toLowerCase.apply(tagToRemove);

        _.remove(tags, function (tag) {
            return tag ===  tagToRemove;
        });
    };

    filter.reset = function () {
      tags = [];
    };
    // Functions a filter must have
    filter.filteredNodes = function () {
        return filteredNodes;
    };

    filter.filteredProperties = function () {
        return filteredProperties;
    };

    return filter;
};
<|endoftext|>


<commit_before>var BATCH_SIZE = 60; // minutes
var MISSING_CANDLES_ALLOWED = 3; // minutes, per batch

var _ = require('lodash');
var moment = require('moment');
var async = require('async');

var util = require('../util');
var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + 'log');

var adapter = config[config.adapter];
var Reader = require(dirs.gekko + adapter.path + '/reader');

var reader = new Reader();

// todo: rewrite with generators or async/await..
var scan = function(done) {
  log.info('Scanning local history for backtestable dateranges.');

  reader.tableExists('candles', (err, exists) => {

    if(err)
      return done(err);

    if(!exists)
      return done(null, []);

    async.parallel({
      boundry: reader.getBoundry,
      available: reader.countTotal
    }, (err, res) => {

      var first = res.boundry.first;
      var last = res.boundry.last;

      var optimal = (last - first) / 60;

      log.debug('Available', res.available);
      log.debug('Optimal', optimal);

      // There is a candle for every minute
      if(res.available === optimal + 1) {
        log.info('Gekko is able to fully use the local history.');
        return done(false, [{
          from: first,
          to: last
        }]);
      }

      // figure out where the gaps are..

      var missing = optimal - res.available + 1;

      log.info(`The database has ${missing} candles missing, Figuring out which ones...`);

      var iterator = {
        from: last - (BATCH_SIZE * 60),
        to: last
      }

      var batches = [];

      // loop through all candles we have
      // in batches and track whether they
      // are complete
      async.whilst(
          () => {
            return iterator.from > first
          },
          next => {
            var from = iterator.from;
            var to = iterator.to;
            reader.count(
              from,
              iterator.to,
              (err, count) => {
                var complete = count + MISSING_CANDLES_ALLOWED > BATCH_SIZE;

                if(complete)
                  batches.push({
                    to: to,
                    from: from
                  });

                next();
              }
            );

            iterator.from -= BATCH_SIZE * 60;
            iterator.to -= BATCH_SIZE * 60;
          },
          () => {

            if(!_.size(batches))
              util.die('Not enough data to work with (please manually set a valid `backtest.daterange`)..', true);

            // batches is now a list like
            // [ {from: unix, to: unix } ]

            var ranges = [ batches.shift() ];

            _.each(batches, batch => {
              var curRange = _.last(ranges);
              if(batch.to === curRange.from)
                curRange.from = batch.from;
              else
                ranges.push( batch );
            })

            // we have been counting chronologically reversed
            // (backwards, from now into the past), flip definitions
            ranges = ranges.reverse();
            _.map(ranges, r => {
              return {
                from: r.to,
                to: r.from
              }
            });

            // ranges is now a list like
            // [ {from: unix, to: unix } ]
            //
            // it contains all valid dataranges available for the
            // end user.

            return done(false, ranges, reader);
          }
        )
    });

  });
}

module.exports = scan;<commit_msg>HOTFIX: make sure to pass reader after early return datascan, fix #701<commit_after>var BATCH_SIZE = 60; // minutes
var MISSING_CANDLES_ALLOWED = 3; // minutes, per batch

var _ = require('lodash');
var moment = require('moment');
var async = require('async');

var util = require('../util');
var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + 'log');

var adapter = config[config.adapter];
var Reader = require(dirs.gekko + adapter.path + '/reader');

var reader = new Reader();

// todo: rewrite with generators or async/await..
var scan = function(done) {
  log.info('Scanning local history for backtestable dateranges.');

  reader.tableExists('candles', (err, exists) => {

    if(err)
      return done(err, null, reader);

    if(!exists)
      return done(null, [], reader);

    async.parallel({
      boundry: reader.getBoundry,
      available: reader.countTotal
    }, (err, res) => {

      var first = res.boundry.first;
      var last = res.boundry.last;

      var optimal = (last - first) / 60;

      log.debug('Available', res.available);
      log.debug('Optimal', optimal);

      // There is a candle for every minute
      if(res.available === optimal + 1) {
        log.info('Gekko is able to fully use the local history.');
        return done(false, [{
          from: first,
          to: last
        }], reader);
      }

      // figure out where the gaps are..

      var missing = optimal - res.available + 1;

      log.info(`The database has ${missing} candles missing, Figuring out which ones...`);

      var iterator = {
        from: last - (BATCH_SIZE * 60),
        to: last
      }

      var batches = [];

      // loop through all candles we have
      // in batches and track whether they
      // are complete
      async.whilst(
          () => {
            return iterator.from > first
          },
          next => {
            var from = iterator.from;
            var to = iterator.to;
            reader.count(
              from,
              iterator.to,
              (err, count) => {
                var complete = count + MISSING_CANDLES_ALLOWED > BATCH_SIZE;

                if(complete)
                  batches.push({
                    to: to,
                    from: from
                  });

                next();
              }
            );

            iterator.from -= BATCH_SIZE * 60;
            iterator.to -= BATCH_SIZE * 60;
          },
          () => {

            if(!_.size(batches))
              util.die('Not enough data to work with (please manually set a valid `backtest.daterange`)..', true);

            // batches is now a list like
            // [ {from: unix, to: unix } ]

            var ranges = [ batches.shift() ];

            _.each(batches, batch => {
              var curRange = _.last(ranges);
              if(batch.to === curRange.from)
                curRange.from = batch.from;
              else
                ranges.push( batch );
            })

            // we have been counting chronologically reversed
            // (backwards, from now into the past), flip definitions
            ranges = ranges.reverse();
            _.map(ranges, r => {
              return {
                from: r.to,
                to: r.from
              }
            });

            // ranges is now a list like
            // [ {from: unix, to: unix } ]
            //
            // it contains all valid dataranges available for the
            // end user.

            return done(false, ranges, reader);
          }
        )
    });

  });
}

module.exports = scan;<|endoftext|>


<commit_before>// Copyright (c) 2007-2011 gocept gmbh & co. kg
// See also LICENSE.txt

gocept.Lightbox = gocept.Class.extend({

    default_options: {
        use_ids: true
    },

    construct: function(parent_element, option_args) {
        var options = MochiKit.Base.clone(this.default_options);
        if (!isUndefinedOrNull(option_args)) {
            MochiKit.Base.update(options, option_args);
        }

        this.parent_element = parent_element;
        this.shade = DIV({'class': 'lightbox-shade'});
        this.header = DIV({'class': 'lightbox-header'});
        this.closebutton = A({'href': '#',
                              'title': 'Close',
                              'class': 'CloseButton'});
        this.content_box = DIV({'class': 'lightbox'});

        if (options.use_ids) {
            forEach([this.shade, this.header, this.content_box],
                    function(element) {
                        element.id = element.getAttribute('class');
            });
        }

        MochiKit.DOM.appendChildNodes(this.header, this.closebutton);
        MochiKit.DOM.appendChildNodes(
            this.parent_element, this.shade, this.header, this.content_box);
        this.events = [];
        this.events.push(
            MochiKit.Signal.connect(
                this.closebutton, 'onclick', this, this.close));
        this.events.push(
            MochiKit.Signal.connect(
                this.shade, 'onclick', this, this.close));
    },

    close: function() {
        MochiKit.Signal.signal(this, 'before-close');
        while (this.events.length) {
            MochiKit.Signal.disconnect(this.events.pop());
        }

        forEach([this.shade, this.header, this.content_box],
                function(element) {
                    MochiKit.Signal.disconnectAll(element);
                    if (!isNull(element.parentNode)) {
                        MochiKit.DOM.removeElement(element);
                    }
        });
    },

    load_url: function(url, query) {
        if (query == undefined) {
            query = {};
        }

        var othis = this;
        if (this.innerHTML == "") {
            this.replace_content("Loading ...");
        }
        var d = MochiKit.Async.doSimpleXMLHttpRequest(url, query);
        d.addCallbacks(
            function(result) {
                return result.responseText;
            },
            function(error) {
                return "There was an error loading the content: " + error;
            });
        d.addCallback(function(result) {
            othis.replace_content(result);
            return result;
        });
        return d;
    },

    replace_content: function(new_html) {
        this.content_box.innerHTML = new_html;
    }

});

zeit.cms.SubPageForm = gocept.Class.extend({

    SUBMIT_DELAY_FOR_FOCUS: 0.01,
    focus_node: null,
    mouse_down: false,

    construct: function(url, container, options) {
        var self = this;
        // initialize defaults
        self.save_on_change = false;
        self.load_immediately = true;
        // update options
        MochiKit.Base.update(self, options);
        self.container = container;
        self.url = url;
        self.events = [];
        self.bind(self.container, 'click', self.handle_click);
        if (self.save_on_change) {
            self.bind(self.container, 'change', self.mark_dirty);
            self.bind(self.container, 'focusin', self.store_focus);
            self.bind(self.container, 'focusout', self.fire_submit);
            self.bind(self.container, 'mousedown', function() {
                self.mouse_down = true;
            });
            self.bind(self.container, 'mouseup', function() {
                self.mouse_down = false;
            });
        }
        self.bind(self.container, 'submit', self.prevent_submit);
        if (self.load_immediately) {
            self.reload();
        } else {
            // our contents have already been rendered by the server,
            // so we only need to do the post-processing and send the
            // appropriate signals.
            self.post_process_html();
            jQuery(self.container).trigger_fragment_ready();
            MochiKit.Signal.signal(self, 'after-reload');
        }
    },

    bind: function(target, event, handler) {
        var self = this;
        handler = MochiKit.Base.bind(handler, self);
        self.events.push([target, event, handler]);
        jQuery(target).bind(event, handler);
    },

    reload: function() {
        var self = this;
        MochiKit.Signal.signal(self, 'before-reload');
        // XXX duplicated from gocept.Lightbox.load_url
        var d = MochiKit.Async.doSimpleXMLHttpRequest(self.url);
        d.addCallbacks(
            function(result) {
                return result.responseText;
            },
            function(error) {
                return "There was an error loading the content: " + error;
            });
        d.addCallback(
            function(result) {
                self.container.innerHTML = result;
                self.post_process_html();
                jQuery(self.container).trigger_fragment_ready();
                MochiKit.Signal.signal(self, 'after-reload');
                MochiKit.DOM.removeElementClass(self.container, 'busy');
                return result;
            });
        d.addErrback(function(err) {zeit.cms.log_error(err); return err;});
        return d;
    },

    close: function() {
        var self = this;
        MochiKit.Signal.signal(self, 'close');
        while(self.events.length) {
            var item = self.events.pop();
            jQuery(item[0]).unbind(item[1], item[2]);
        }
    },

    handle_click: function(event) {
        var self = this;
        var target = event.target;

        if (target.nodeName == 'INPUT' && target.type == 'button'
            && MochiKit.DOM.hasElementClass(target, 'submit')) {
            self.handle_submit(target.name);
            event.stopPropagation();
        }

        if (self.save_on_change
            && target.nodeName == 'INPUT' && target.type == 'checkbox') {
            self.handle_submit();
        }
    },

    prevent_submit: function(event) {
        var self = this;
        if (!isNull(self.form)) {
            self.handle_submit();
            return false;
       }
    },

    mark_dirty: function(event) {
        var self = this;
        var target = event.target;

        if (zeit.cms.in_array(
            target.nodeName, ['INPUT', 'TEXTAREA', 'SELECT'])) {
            var outer = MochiKit.DOM.getFirstParentByTagAndClassName(
                target, null, 'field');
            MochiKit.DOM.addElementClass(outer, 'dirty');
            if (target.nodeName == 'INPUT' && target.type == 'hidden') {
                self.handle_submit();
            }
        }
    },

    store_focus: function(event) {
        var self = this;
        var target = event.target;
        if (zeit.cms.in_array(
            target.nodeName, ['INPUT', 'TEXTAREA', 'SELECT'])) {
            self.focus_node = target;
        }
    },

    fire_submit: function(event) {
        var self = this;
        var target = event.target;
        if (!zeit.cms.in_array(
                target.nodeName, ['INPUT', 'TEXTAREA', 'SELECT']) ||
             self.mouse_down) {
            return;
        }

        self.focus_node = null;
        MochiKit.Async.callLater(self.SUBMIT_DELAY_FOR_FOCUS, function() {
            // If a field of our form has the focus now, we don't want to
            // interrupt the user by saving.
            if (self.focus_node === null) {
                self.handle_submit();
            }
        });
    },

    handle_submit: function(action) {
        var self = this;

        // collect data
        var elements = filter(
            function(element) {
                return (element.type != 'button');
            }, self.form.elements);

        var data = map(function(element) {
                if ((element.type == 'radio' || element.type == 'checkbox') &&
                    !element.checked) {
                    return;
                }
                return element.name + "=" + encodeURIComponent(element.value);
            }, elements);
        if (isUndefinedOrNull(action)) {
            var button = MochiKit.Selector.findChildElements(
                self.container, ['.form-controls input'])[0];
            action = button.name;
        }
        data.push(action + '=clicked');
        data = data.join('&');
        var submit_to = self.form.getAttribute('action');

        // clear box with loading message
        self.loading();

        var d = zeit.cms.locked_xhr(submit_to, {
            'method': 'POST',
            'headers': {
                'Content-Type': 'application/x-www-form-urlencoded'},
            'sendContent': data});
        d.addCallbacks(
            MochiKit.Base.bind(self.replace_content, self),
            function(error) {
                logError(error.req.status, error.req.statusText);
                var parser = new DOMParser();
                var doc = parser.parseFromString(
                    error.req.responseText, "text/xml");
                document.firstChild.nextSibling.nextSibling.innerHTML = doc.firstChild.nextSibling.innerHTML;
            });
        d.addCallback(MochiKit.Base.bind(self.process_post_result, self));
        d.addCallback(function(result) {
            if (result === null) {
                return null;
            }
            // XXX this is the third place that calls post_process_html and
            // sends signals -- refactor! (#11270).
            self.post_process_html();
            jQuery(self.container).trigger_fragment_ready();
            MochiKit.Signal.signal(window, 'changed', self);
            MochiKit.Signal.signal(self, 'after-reload');
            // Delaying the class remove somehow avoids flickering
            MochiKit.Async.callLater(0,
                MochiKit.DOM.removeElementClass, self.container, 'busy');
            return result;
        });
        d.addErrback(function(err) {zeit.cms.log_error(err); return err;});
        return d;
    },

    replace_content: function(result) {
        var self = this;
        self.container.innerHTML = result.responseText;
        return result;
    },

    process_post_result: function(result) {
        var self = this;
        if (self.has_errors()) {
            return result;
        }
        var next_url = self.get_next_url();
        if (isNull(next_url)) {
            return result;
        }
        window.location = next_url;
        return null;
    },

    has_errors: function(result) {
        var self = this;
        var errors = MochiKit.DOM.getFirstElementByTagAndClassName(
            'ul', 'errors', self.container);
        return (errors != null);
    },

    get_next_url: function() {
        var self = this;
        var next_url_node = MochiKit.DOM.getFirstElementByTagAndClassName(
            'span', 'nextURL', self.container);
        if (isNull(next_url_node)) {
            return null;
        }
        return next_url_node.textContent;
    },

    loading: function(message) {
        var self = this;
        if (!isUndefinedOrNull(message)) {
            log('messssage', repr(message));
            self.container.innerHTML = message;
        }
        MochiKit.DOM.addElementClass(self.container, 'busy');
    },

    post_process_html: function() {
        var self = this;
        if (self.container.nodeName == 'FORM') {
            self.form = self.container;
        } else {
            self.form = MochiKit.DOM.getFirstElementByTagAndClassName(
                'form', null, self.container);
        }
        self.rewire_submit_buttons();
        zeit.cms.evaluate_js_and_css(
            self.container, function(code) { eval(code); });
    },

    rewire_submit_buttons: function() {
        // Change all submits to buttons to be able to handle them in
        // java script
        var self = this;
        forEach(
            MochiKit.DOM.getElementsByTagAndClassName(
                'input', null, self.container),
            function(button) {
                if (button.type == 'submit') {
                    button.type = 'button';
                    MochiKit.DOM.addElementClass(button, 'submit');
                }
            });
    }
});

zeit.cms.LightboxForm = zeit.cms.SubPageForm.extend({

    construct: function(url, container) {
        var self = this;
        if (isUndefinedOrNull(container)) {
            container = jQuery('body')[0];
        }
        self.lightbox = self.create_lightbox(container);
        arguments.callee.$.construct.call(self, url, self.lightbox.content_box);
        self.mochi_events = [];
        self.mochi_events.push(
            MochiKit.Signal.connect(
                window, 'zeit.cms.LightboxReload', function(event) {
                self.loading();
            }));
        self.mochi_events.push(
            MochiKit.Signal.connect(
                self.lightbox, 'before-close', self, self.close));
    },

    create_lightbox: function(container) {
        //var self = this;
        return new gocept.Lightbox(container, {
            use_ids: false
        });
    },

    reload: function() {
        var self = this;
        var d = self.lightbox.load_url(self.url);
        d.addCallback(function(result) {
            self.post_process_html();
            return result;
        });
        d.addErrback(function(err) {zeit.cms.log_error(err); return err;});
        return d;
    },

    close: function() {
        var self = this;
        arguments.callee.$.close.call(self);
         while(self.mochi_events.length) {
             MochiKit.Signal.disconnect(self.mochi_events.pop());
         }
        self.lightbox.close();
    }
});

zeit.cms.lightbox_form = function(url) {
    new zeit.cms.LightboxForm(url);
};
<commit_msg>coding style<commit_after>// Copyright (c) 2007-2011 gocept gmbh & co. kg
// See also LICENSE.txt

gocept.Lightbox = gocept.Class.extend({

    default_options: {
        use_ids: true
    },

    construct: function(parent_element, option_args) {
        var options = MochiKit.Base.clone(this.default_options);
        if (!isUndefinedOrNull(option_args)) {
            MochiKit.Base.update(options, option_args);
        }

        this.parent_element = parent_element;
        this.shade = DIV({'class': 'lightbox-shade'});
        this.header = DIV({'class': 'lightbox-header'});
        this.closebutton = A({'href': '#',
                              'title': 'Close',
                              'class': 'CloseButton'});
        this.content_box = DIV({'class': 'lightbox'});

        if (options.use_ids) {
            forEach([this.shade, this.header, this.content_box],
                    function(element) {
                        element.id = element.getAttribute('class');
            });
        }

        MochiKit.DOM.appendChildNodes(this.header, this.closebutton);
        MochiKit.DOM.appendChildNodes(
            this.parent_element, this.shade, this.header, this.content_box);
        this.events = [];
        this.events.push(
            MochiKit.Signal.connect(
                this.closebutton, 'onclick', this, this.close));
        this.events.push(
            MochiKit.Signal.connect(
                this.shade, 'onclick', this, this.close));
    },

    close: function() {
        MochiKit.Signal.signal(this, 'before-close');
        while (this.events.length) {
            MochiKit.Signal.disconnect(this.events.pop());
        }

        forEach([this.shade, this.header, this.content_box],
                function(element) {
                    MochiKit.Signal.disconnectAll(element);
                    if (!isNull(element.parentNode)) {
                        MochiKit.DOM.removeElement(element);
                    }
        });
    },

    load_url: function(url, query) {
        if (query == undefined) {
            query = {};
        }

        var othis = this;
        if (this.innerHTML == "") {
            this.replace_content("Loading ...");
        }
        var d = MochiKit.Async.doSimpleXMLHttpRequest(url, query);
        d.addCallbacks(
            function(result) {
                return result.responseText;
            },
            function(error) {
                return "There was an error loading the content: " + error;
            });
        d.addCallback(function(result) {
            othis.replace_content(result);
            return result;
        });
        return d;
    },

    replace_content: function(new_html) {
        this.content_box.innerHTML = new_html;
    }

});

zeit.cms.SubPageForm = gocept.Class.extend({

    SUBMIT_DELAY_FOR_FOCUS: 0.01,
    focus_node: null,
    mouse_down: false,

    construct: function(url, container, options) {
        var self = this;
        // initialize defaults
        self.save_on_change = false;
        self.load_immediately = true;
        // update options
        MochiKit.Base.update(self, options);
        self.container = container;
        self.url = url;
        self.events = [];
        self.bind(self.container, 'click', self.handle_click);
        if (self.save_on_change) {
            self.bind(self.container, 'change', self.mark_dirty);
            self.bind(self.container, 'focusin', self.store_focus);
            self.bind(self.container, 'focusout', self.fire_submit);
            self.bind(self.container, 'mousedown', function() {
                self.mouse_down = true;
            });
            self.bind(self.container, 'mouseup', function() {
                self.mouse_down = false;
            });
        }
        self.bind(self.container, 'submit', self.prevent_submit);
        if (self.load_immediately) {
            self.reload();
        } else {
            // our contents have already been rendered by the server,
            // so we only need to do the post-processing and send the
            // appropriate signals.
            self.post_process_html();
            jQuery(self.container).trigger_fragment_ready();
            MochiKit.Signal.signal(self, 'after-reload');
        }
    },

    bind: function(target, event, handler) {
        var self = this;
        handler = MochiKit.Base.bind(handler, self);
        self.events.push([target, event, handler]);
        jQuery(target).bind(event, handler);
    },

    reload: function() {
        var self = this;
        MochiKit.Signal.signal(self, 'before-reload');
        // XXX duplicated from gocept.Lightbox.load_url
        var d = MochiKit.Async.doSimpleXMLHttpRequest(self.url);
        d.addCallbacks(
            function(result) {
                return result.responseText;
            },
            function(error) {
                return "There was an error loading the content: " + error;
            });
        d.addCallback(
            function(result) {
                self.container.innerHTML = result;
                self.post_process_html();
                jQuery(self.container).trigger_fragment_ready();
                MochiKit.Signal.signal(self, 'after-reload');
                MochiKit.DOM.removeElementClass(self.container, 'busy');
                return result;
            });
        d.addErrback(function(err) {zeit.cms.log_error(err); return err;});
        return d;
    },

    close: function() {
        var self = this;
        MochiKit.Signal.signal(self, 'close');
        while(self.events.length) {
            var item = self.events.pop();
            jQuery(item[0]).unbind(item[1], item[2]);
        }
    },

    handle_click: function(event) {
        var self = this;
        var target = event.target;

        if (target.nodeName == 'INPUT' &&
            target.type == 'button' &&
            MochiKit.DOM.hasElementClass(target, 'submit')) {
            self.handle_submit(target.name);
            event.stopPropagation();
        }

        if (self.save_on_change &&
            target.nodeName == 'INPUT' &&
            target.type == 'checkbox') {
            self.handle_submit();
        }
    },

    prevent_submit: function(event) {
        var self = this;
        if (!isNull(self.form)) {
            self.handle_submit();
            return false;
       }
    },

    mark_dirty: function(event) {
        var self = this;
        var target = event.target;

        if (zeit.cms.in_array(
            target.nodeName, ['INPUT', 'TEXTAREA', 'SELECT'])) {
            var outer = MochiKit.DOM.getFirstParentByTagAndClassName(
                target, null, 'field');
            MochiKit.DOM.addElementClass(outer, 'dirty');
            if (target.nodeName == 'INPUT' && target.type == 'hidden') {
                self.handle_submit();
            }
        }
    },

    store_focus: function(event) {
        var self = this;
        var target = event.target;
        if (zeit.cms.in_array(
            target.nodeName, ['INPUT', 'TEXTAREA', 'SELECT'])) {
            self.focus_node = target;
        }
    },

    fire_submit: function(event) {
        var self = this;
        var target = event.target;
        if (!zeit.cms.in_array(
                target.nodeName, ['INPUT', 'TEXTAREA', 'SELECT']) ||
             self.mouse_down) {
            return;
        }

        self.focus_node = null;
        MochiKit.Async.callLater(self.SUBMIT_DELAY_FOR_FOCUS, function() {
            // If a field of our form has the focus now, we don't want to
            // interrupt the user by saving.
            if (self.focus_node === null) {
                self.handle_submit();
            }
        });
    },

    handle_submit: function(action) {
        var self = this;

        // collect data
        var elements = filter(
            function(element) {
                return (element.type != 'button');
            }, self.form.elements);

        var data = map(function(element) {
                if ((element.type == 'radio' || element.type == 'checkbox') &&
                    !element.checked) {
                    return;
                }
                return element.name + "=" + encodeURIComponent(element.value);
            }, elements);
        if (isUndefinedOrNull(action)) {
            var button = MochiKit.Selector.findChildElements(
                self.container, ['.form-controls input'])[0];
            action = button.name;
        }
        data.push(action + '=clicked');
        data = data.join('&');
        var submit_to = self.form.getAttribute('action');

        // clear box with loading message
        self.loading();

        var d = zeit.cms.locked_xhr(submit_to, {
            'method': 'POST',
            'headers': {
                'Content-Type': 'application/x-www-form-urlencoded'},
            'sendContent': data});
        d.addCallbacks(
            MochiKit.Base.bind(self.replace_content, self),
            function(error) {
                logError(error.req.status, error.req.statusText);
                var parser = new DOMParser();
                var doc = parser.parseFromString(
                    error.req.responseText, "text/xml");
                document.firstChild.nextSibling.nextSibling.innerHTML = doc.firstChild.nextSibling.innerHTML;
            });
        d.addCallback(MochiKit.Base.bind(self.process_post_result, self));
        d.addCallback(function(result) {
            if (result === null) {
                return null;
            }
            // XXX this is the third place that calls post_process_html and
            // sends signals -- refactor! (#11270).
            self.post_process_html();
            jQuery(self.container).trigger_fragment_ready();
            MochiKit.Signal.signal(window, 'changed', self);
            MochiKit.Signal.signal(self, 'after-reload');
            // Delaying the class remove somehow avoids flickering
            MochiKit.Async.callLater(0,
                MochiKit.DOM.removeElementClass, self.container, 'busy');
            return result;
        });
        d.addErrback(function(err) {zeit.cms.log_error(err); return err;});
        return d;
    },

    replace_content: function(result) {
        var self = this;
        self.container.innerHTML = result.responseText;
        return result;
    },

    process_post_result: function(result) {
        var self = this;
        if (self.has_errors()) {
            return result;
        }
        var next_url = self.get_next_url();
        if (isNull(next_url)) {
            return result;
        }
        window.location = next_url;
        return null;
    },

    has_errors: function(result) {
        var self = this;
        var errors = MochiKit.DOM.getFirstElementByTagAndClassName(
            'ul', 'errors', self.container);
        return (errors != null);
    },

    get_next_url: function() {
        var self = this;
        var next_url_node = MochiKit.DOM.getFirstElementByTagAndClassName(
            'span', 'nextURL', self.container);
        if (isNull(next_url_node)) {
            return null;
        }
        return next_url_node.textContent;
    },

    loading: function(message) {
        var self = this;
        if (!isUndefinedOrNull(message)) {
            log('messssage', repr(message));
            self.container.innerHTML = message;
        }
        MochiKit.DOM.addElementClass(self.container, 'busy');
    },

    post_process_html: function() {
        var self = this;
        if (self.container.nodeName == 'FORM') {
            self.form = self.container;
        } else {
            self.form = MochiKit.DOM.getFirstElementByTagAndClassName(
                'form', null, self.container);
        }
        self.rewire_submit_buttons();
        zeit.cms.evaluate_js_and_css(
            self.container, function(code) { eval(code); });
    },

    rewire_submit_buttons: function() {
        // Change all submits to buttons to be able to handle them in
        // java script
        var self = this;
        forEach(
            MochiKit.DOM.getElementsByTagAndClassName(
                'input', null, self.container),
            function(button) {
                if (button.type == 'submit') {
                    button.type = 'button';
                    MochiKit.DOM.addElementClass(button, 'submit');
                }
            });
    }
});

zeit.cms.LightboxForm = zeit.cms.SubPageForm.extend({

    construct: function(url, container) {
        var self = this;
        if (isUndefinedOrNull(container)) {
            container = jQuery('body')[0];
        }
        self.lightbox = self.create_lightbox(container);
        arguments.callee.$.construct.call(self, url, self.lightbox.content_box);
        self.mochi_events = [];
        self.mochi_events.push(
            MochiKit.Signal.connect(
                window, 'zeit.cms.LightboxReload', function(event) {
                self.loading();
            }));
        self.mochi_events.push(
            MochiKit.Signal.connect(
                self.lightbox, 'before-close', self, self.close));
    },

    create_lightbox: function(container) {
        //var self = this;
        return new gocept.Lightbox(container, {
            use_ids: false
        });
    },

    reload: function() {
        var self = this;
        var d = self.lightbox.load_url(self.url);
        d.addCallback(function(result) {
            self.post_process_html();
            return result;
        });
        d.addErrback(function(err) {zeit.cms.log_error(err); return err;});
        return d;
    },

    close: function() {
        var self = this;
        arguments.callee.$.close.call(self);
         while(self.mochi_events.length) {
             MochiKit.Signal.disconnect(self.mochi_events.pop());
         }
        self.lightbox.close();
    }
});

zeit.cms.lightbox_form = function(url) {
    new zeit.cms.LightboxForm(url);
};
<|endoftext|>


<commit_before>/*global: WebSocket */
define([
    '/bower_components/socket.io-client/dist/socket.io.slim.js',
    '/bower_components/es6-promise/es6-promise.min.js',
],function (io) {
    'use strict';

    // How much lag before we send a ping
    var MAX_LAG_BEFORE_PING = 15000;

    // How much of a lag we accept before we will drop the socket
    var MAX_LAG_BEFORE_DISCONNECT = 30000;

    // How often to ping the server
    var PING_CYCLE = 5000;

    // How long before we decide we could not make a websocket to the server.
    var REQUEST_TIMEOUT = 30000;

    // If we try to close the websocket but it fails to close,
    // how long before we discard the socket and start a new one.
    var FORCE_CLOSE_TIMEOUT = 10000;

    // If we are unable to connect to the server (don't get a connection),
    // this is how often we will retry to connect.
    var RECONNECT_LOOP_CYCLE = 7000;

    var NOFUNC = function () {};

    var now = function now() {
        return new Date().getTime();
    };

    var getLag = function (ctx) {
        if (!ctx.ws) { return null; }
        if (!ctx.pingOutstanding) { return ctx.lastObservedLag; }
        return Math.max(now() - ctx.timeOfLastPingSent, ctx.lastObservedLag);
    };

    var setTimeoutX = function (ctx, func, time) {
        ctx.timeouts.push(setTimeout(func, time));
    };

    var closeWebsocket = function (ctx) {
        if (!ctx.ws) { return; }
        ctx.ws.close();
    };

    var send = function (ctx, content) {
        if (!ctx.ws) { throw new Error("Disconnected, you cannot send any message right now"); }
        ctx.ws.send(JSON.stringify(content));
    }

    var networkSendTo = function networkSendTo(ctx, peerId, content) {
        var seq = ctx.seq++;
        send(ctx, [seq, 'MSG', peerId, content]);
        return new Promise(function (res, rej) {
            ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
        });
    };

    var channelBcast = function channelBcast(ctx, chanId, content) {
        var chan = ctx.channels[chanId];
        if (!chan) {
            throw new Error("no such channel " + chanId);
        }
        var seq = ctx.seq++;
        send(ctx, [seq, 'MSG', chanId, content]);
        return new Promise(function (res, rej) {
            ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
        });
    };

    var channelLeave = function channelLeave(ctx, chanId, reason) {
        var chan = ctx.channels[chanId];
        if (!chan) {
            throw new Error("no such channel " + chanId);
        }
        var seq = ctx.seq++;
        delete ctx.channels[chanId];
        if (ctx.ws.readyState !== 1) { return; } // the websocket connection is not opened
        send(ctx, [seq, 'LEAVE', chanId, reason]);
        var emptyFunction = function() {};
        ctx.requests[seq] = { reject: emptyFunction, resolve: emptyFunction, time: now() };
    };

    var makeEventHandlers = function makeEventHandlers(ctx, mappings) {
        return function (name, handler) {
            var handlers = mappings[name];
            if (!handlers) {
                throw new Error("no such event " + name);
            }
            handlers.push(handler);
        };
    };

    var mkChannel = function mkChannel(ctx, id) {
        var internal = {
            onMessage: [],
            onJoin: [],
            onLeave: [],
            members: [],
            jSeq: ctx.seq++
        };
        var chan = {
            _: internal,
            time: now(),
            id: id,
            members: internal.members,
            bcast: function bcast(msg) {
                return channelBcast(ctx, chan.id, msg);
            },
            leave: function leave(reason) {
                return channelLeave(ctx, chan.id, reason);
            },
            on: makeEventHandlers(ctx, { message: internal.onMessage, join: internal.onJoin, leave: internal.onLeave })
        };
        ctx.requests[internal.jSeq] = chan;
        send(ctx, [internal.jSeq, 'JOIN', id]);

        return new Promise(function (res, rej) {
            chan._.resolve = res;
            chan._.reject = rej;
        });
    };

    var disconnect = function (ctx) {
        if (ctx.ws) {
            var onclose = ctx.ws.onclose;
            ctx.ws.onclose = NOFUNC;
            ctx.ws.close();
            // this reason string is matched in other code so don't change it.
            onclose({ reason: "network.disconnect() called" });
        }
        ctx.timeouts.forEach(clearTimeout);
        ctx.timeouts = [];
    };

    var mkNetwork = function mkNetwork(ctx) {
        var network = {
            webChannels: ctx.channels,
            getLag: function _getLag() {
                return getLag(ctx);
            },
            sendto: function sendto(peerId, content) {
                return networkSendTo(ctx, peerId, content);
            },
            join: function join(chanId) {
                return mkChannel(ctx, chanId);
            },
            disconnect: function () {
                return disconnect(ctx);
            },
            on: makeEventHandlers(ctx, { message: ctx.onMessage, disconnect: ctx.onDisconnect, reconnect: ctx.onReconnect })
        };
        network.__defineGetter__("webChannels", function () {
            return Object.keys(ctx.channels).map(function (k) {
                return ctx.channels[k];
            });
        });
        return network;
    };

    var onMessage = function onMessage(ctx, evt) {
        var msg = void 0;
        try {
            msg = JSON.parse(evt.data);
        } catch (e) {
            console.log(e.stack);return;
        }
        if (msg[0] !== 0) {
            var req = ctx.requests[msg[0]];
            if (!req) {
                console.log("error: " + JSON.stringify(msg));
                return;
            }
            delete ctx.requests[msg[0]];
            if (msg[1] === 'ACK') {
                if (req.ping) {
                    // ACK of a PING
                    ctx.lastObservedLag = now() - Number(req.ping);
                    ctx.timeOfLastPingReceived = now();
                    ctx.pingOutstanding--;
                    return;
                }
                req.resolve();
            } else if (msg[1] === 'JACK') {
                if (req._) {
                    // Channel join request...
                    if (!msg[2]) {
                        throw new Error("wrong type of ACK for channel join");
                    }
                    req.id = msg[2];
                    ctx.channels[req.id] = req;
                    return;
                }
                req.resolve();
            } else if (msg[1] === 'ERROR') {
                if (typeof req.reject === "function") {
                    req.reject({ type: msg[2], message: msg[3] });
                } else {
                    console.error(msg);
                }
            } else {
                req.reject({ type: 'UNKNOWN', message: JSON.stringify(msg) });
            }
            return;
        }

        if (msg[2] === 'IDENT') {
            ctx.uid = msg[3];
            ctx.ws._onident();
            ctx.pingInterval = setInterval(function () {
                if (now() - ctx.timeOfLastPingReceived < MAX_LAG_BEFORE_PING) { return; }
                if (now() - ctx.timeOfLastPingReceived > MAX_LAG_BEFORE_DISCONNECT) {
                    closeWebsocket(ctx);
                }
                if (ctx.pingOutstanding) { return; }
                var seq = ctx.seq++;
                var currentDate = now();
                ctx.timeOfLastPingSent = currentDate;
                ctx.pingOutstanding++;
                ctx.requests[seq] = { time: currentDate, ping: currentDate };
                send(ctx, [seq, 'PING']);
            }, PING_CYCLE);

            return;
        } else if (!ctx.uid) {
            // extranious message, waiting for an ident.
            return;
        }
        if (msg[2] === 'PING') {
            msg[2] = 'PONG';
            send(ctx, msg);
            return;
        }

        if (msg[2] === 'MSG') {
            var handlers = void 0;
            if (msg[3] === ctx.uid) {
                handlers = ctx.onMessage;
            } else {
                var chan = ctx.channels[msg[3]];
                if (!chan) {
                    console.log("message to non-existant chan " + JSON.stringify(msg));
                    return;
                }
                handlers = chan._.onMessage;
            }
            handlers.forEach(function (h) {
                try {
                    h(msg[4], msg[1]);
                } catch (e) {
                    console.error(e);
                }
            });
        }

        if (msg[2] === 'LEAVE') {
            var _chan = ctx.channels[msg[3]];
            if (!_chan) {
                console.log("leaving non-existant chan " + JSON.stringify(msg));
                return;
            }
            _chan._.onLeave.forEach(function (h) {
                try {
                    h(msg[1], msg[4]);
                } catch (e) {
                    console.log(e.stack);
                }
            });
        }

        if (msg[2] === 'JOIN') {
            var _chan2 = ctx.channels[msg[3]];
            if (!_chan2) {
                console.log("ERROR: join to non-existant chan " + JSON.stringify(msg));
                return;
            }
            // have we yet fully joined the chan?
            var synced = _chan2._.members.indexOf(ctx.uid) !== -1;
            _chan2._.members.push(msg[1]);
            if (!synced && msg[1] === ctx.uid) {
                // sync the channel join event
                _chan2.myID = ctx.uid;
                _chan2._.resolve(_chan2);
            }
            if (synced) {
                _chan2._.onJoin.forEach(function (h) {
                    try {
                        h(msg[1]);
                    } catch (e) {
                        console.log(e.stack);
                    }
                });
            }
        }
    };

    var splitUrl = function (url) {
        var arr = /^((?:wss?:\/\/)?(?:www\.)?(?:[^\/\n]+))(\/?.+)/.exec(url);
        return {
            url: arr[0],
            base: arr[1],
            path: arr[2]
        }
    };

    var connect = function connect(websocketURL, makeWebsocket) {
        makeWebsocket = makeWebsocket || function (url) {
            var urlObj = splitUrl(url);
            return io(urlObj.base, {
                path: urlObj.path,
                //transports: ['websocket']
            });
        };
        var ctx = {
            ws: null,
            seq: 1,
            uid: null,
            network: null,
            channels: {},
            onMessage: [],
            onDisconnect: [],
            onReconnect: [],
            timeouts: [],
            requests: {},
            pingInterval: null,

            timeOfLastPingSent: -1,
            timeOfLastPingReceived: -1,
            lastObservedLag: 0,
            pingOutstanding: 0
        };
        ctx.network = mkNetwork(ctx);

        // For the returned promise, after the promise is resolved/rejected these will be NOFUNC'd.
        var promiseResolve = NOFUNC;
        var promiseReject = NOFUNC;

        var connectWs = function () {
            var ws = ctx.ws = makeWebsocket(websocketURL);
            ctx.timeOfLastPingSent = ctx.timeOfLastPingReceived = now();
            ws.on('message', function (msg) { return onMessage(ctx, {data:msg}); });
            ws.on('disconnect', function (reason) {
                if (reason === 'io server disconnect') {
                    // the disconnection was initiated by the server, you need to reconnect manually
                    ws.connect();
                }
                // else the socket will automatically try to reconnect

                clearInterval(ctx.pingInterval);
                ctx.timeouts.forEach(clearTimeout);
                if (ctx.uid) {
                    ctx.uid = null;
                    ctx.onDisconnect.forEach(function (h) {
                        try {
                            h(reason);
                        } catch (e) {
                            console.log(e.stack);
                        }
                    });
                }
                // Try to reconnect...
                //setTimeoutX(ctx, connectWs, (ctx.uid) ? 0 : RECONNECT_LOOP_CYCLE);
            });
            ws.on('connect', function () {
                setTimeoutX(ctx, function () {
                    if (ctx.uid) { return; }
                    promiseReject({ type: 'TIMEOUT', message: 'waited ' + REQUEST_TIMEOUT + 'ms' });
                    promiseResolve = promiseReject = NOFUNC;
                    closeWebsocket(ctx);
                }, REQUEST_TIMEOUT);
            });
            ctx.ws._onident = function () {
                // This is to use the time of IDENT minus the connect time to guess a ping reception
                ctx.timeOfLastPingReceived = now();
                ctx.lastObservedLag = now() - ctx.timeOfLastPingSent;

                if (promiseResolve !== NOFUNC) {
                    promiseResolve(ctx.network);
                    promiseResolve = promiseReject = NOFUNC;
                } else {
                    ctx.channels = {};
                    ctx.requests = {};
                    ctx.pingOutstanding = 0;
                    ctx.onReconnect.forEach(function (h) {
                       try {
                            h(ctx.uid);
                        } catch (e) {
                            console.log(e.stack);
                        }
                    });
                }
            }
        };

        return new Promise(function (resolve, reject) {
            promiseResolve = resolve;
            promiseReject = reject;
            connectWs();
        });
    };

    return { connect: connect };
});
<commit_msg>Fix missing features from socket.io migration<commit_after>/*global: WebSocket */
define([
    '/bower_components/socket.io-client/dist/socket.io.slim.js',
    '/bower_components/es6-promise/es6-promise.min.js',
],function (io) {
    'use strict';

    // How much lag before we send a ping
    var MAX_LAG_BEFORE_PING = 15000;

    // How much of a lag we accept before we will drop the socket
    var MAX_LAG_BEFORE_DISCONNECT = 30000;

    // How often to ping the server
    var PING_CYCLE = 5000;

    // How long before we decide we could not make a websocket to the server.
    var REQUEST_TIMEOUT = 30000;

    // If we try to close the websocket but it fails to close,
    // how long before we discard the socket and start a new one.
    var FORCE_CLOSE_TIMEOUT = 10000;

    // If we are unable to connect to the server (don't get a connection),
    // this is how often we will retry to connect.
    var RECONNECT_LOOP_CYCLE = 7000;

    var NOFUNC = function () {};

    var now = function now() {
        return new Date().getTime();
    };

    var getLag = function (ctx) {
        if (!ctx.ws) { return null; }
        if (!ctx.pingOutstanding) { return ctx.lastObservedLag; }
        return Math.max(now() - ctx.timeOfLastPingSent, ctx.lastObservedLag);
    };

    var setTimeoutX = function (ctx, func, time) {
        ctx.timeouts.push(setTimeout(func, time));
    };

    var closeWebsocket = function (ctx) {
        if (!ctx.ws) { return; }
        ctx.ws.close();
    };

    var send = function (ctx, content) {
        if (!ctx.ws) { throw new Error("Disconnected, you cannot send any message right now"); }
        ctx.ws.send(JSON.stringify(content));
    }

    var networkSendTo = function networkSendTo(ctx, peerId, content) {
        var seq = ctx.seq++;
        send(ctx, [seq, 'MSG', peerId, content]);
        return new Promise(function (res, rej) {
            ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
        });
    };

    var channelBcast = function channelBcast(ctx, chanId, content) {
        var chan = ctx.channels[chanId];
        if (!chan) {
            throw new Error("no such channel " + chanId);
        }
        var seq = ctx.seq++;
        send(ctx, [seq, 'MSG', chanId, content]);
        return new Promise(function (res, rej) {
            ctx.requests[seq] = { reject: rej, resolve: res, time: now() };
        });
    };

    var channelLeave = function channelLeave(ctx, chanId, reason) {
        var chan = ctx.channels[chanId];
        if (!chan) {
            throw new Error("no such channel " + chanId);
        }
        var seq = ctx.seq++;
        delete ctx.channels[chanId];
        if (!ctx.ws || !ctx.ws.connected) { return; } // the websocket connection is not opened
        send(ctx, [seq, 'LEAVE', chanId, reason]);
        var emptyFunction = function() {};
        ctx.requests[seq] = { reject: emptyFunction, resolve: emptyFunction, time: now() };
    };

    var makeEventHandlers = function makeEventHandlers(ctx, mappings) {
        return function (name, handler) {
            var handlers = mappings[name];
            if (!handlers) {
                throw new Error("no such event " + name);
            }
            handlers.push(handler);
        };
    };

    var mkChannel = function mkChannel(ctx, id) {
        var internal = {
            onMessage: [],
            onJoin: [],
            onLeave: [],
            members: [],
            jSeq: ctx.seq++
        };
        var chan = {
            _: internal,
            time: now(),
            id: id,
            members: internal.members,
            bcast: function bcast(msg) {
                return channelBcast(ctx, chan.id, msg);
            },
            leave: function leave(reason) {
                return channelLeave(ctx, chan.id, reason);
            },
            on: makeEventHandlers(ctx, { message: internal.onMessage, join: internal.onJoin, leave: internal.onLeave })
        };
        ctx.requests[internal.jSeq] = chan;
        send(ctx, [internal.jSeq, 'JOIN', id]);

        return new Promise(function (res, rej) {
            chan._.resolve = res;
            chan._.reject = rej;
        });
    };

    var isIntentionallyLeaving = false;
    var disconnect = function (ctx) {
        if (ctx.ws) {
            isIntentionallyLeaving = true;
            ctx.ws.close();
        }
        ctx.timeouts.forEach(clearTimeout);
        ctx.timeouts = [];
    };

    var mkNetwork = function mkNetwork(ctx) {
        var network = {
            webChannels: ctx.channels,
            getLag: function _getLag() {
                return getLag(ctx);
            },
            sendto: function sendto(peerId, content) {
                return networkSendTo(ctx, peerId, content);
            },
            join: function join(chanId) {
                return mkChannel(ctx, chanId);
            },
            disconnect: function () {
                return disconnect(ctx);
            },
            on: makeEventHandlers(ctx, { message: ctx.onMessage, disconnect: ctx.onDisconnect, reconnect: ctx.onReconnect })
        };
        network.__defineGetter__("webChannels", function () {
            return Object.keys(ctx.channels).map(function (k) {
                return ctx.channels[k];
            });
        });
        return network;
    };

    var onMessage = function onMessage(ctx, evt) {
        var msg = void 0;
        try {
            msg = JSON.parse(evt.data);
        } catch (e) {
            console.log(e.stack);return;
        }
        if (msg[0] !== 0) {
            var req = ctx.requests[msg[0]];
            if (!req) {
                console.log("error: " + JSON.stringify(msg));
                return;
            }
            delete ctx.requests[msg[0]];
            if (msg[1] === 'ACK') {
                if (req.ping) {
                    // ACK of a PING
                    ctx.lastObservedLag = now() - Number(req.ping);
                    ctx.timeOfLastPingReceived = now();
                    ctx.pingOutstanding--;
                    return;
                }
                req.resolve();
            } else if (msg[1] === 'JACK') {
                if (req._) {
                    // Channel join request...
                    if (!msg[2]) {
                        throw new Error("wrong type of ACK for channel join");
                    }
                    req.id = msg[2];
                    ctx.channels[req.id] = req;
                    return;
                }
                req.resolve();
            } else if (msg[1] === 'ERROR') {
                if (typeof req.reject === "function") {
                    req.reject({ type: msg[2], message: msg[3] });
                } else {
                    console.error(msg);
                }
            } else {
                req.reject({ type: 'UNKNOWN', message: JSON.stringify(msg) });
            }
            return;
        }

        if (msg[2] === 'IDENT') {
            ctx.uid = msg[3];
            ctx.ws._onident();
            ctx.pingInterval = setInterval(function () {
                if (now() - ctx.timeOfLastPingReceived < MAX_LAG_BEFORE_PING) { return; }
                if (now() - ctx.timeOfLastPingReceived > MAX_LAG_BEFORE_DISCONNECT) {
                    closeWebsocket(ctx);
                }
                if (ctx.pingOutstanding) { return; }
                var seq = ctx.seq++;
                var currentDate = now();
                ctx.timeOfLastPingSent = currentDate;
                ctx.pingOutstanding++;
                ctx.requests[seq] = { time: currentDate, ping: currentDate };
                send(ctx, [seq, 'PING']);
            }, PING_CYCLE);

            return;
        } else if (!ctx.uid) {
            // extranious message, waiting for an ident.
            return;
        }
        if (msg[2] === 'PING') {
            msg[2] = 'PONG';
            send(ctx, msg);
            return;
        }

        if (msg[2] === 'MSG') {
            var handlers = void 0;
            if (msg[3] === ctx.uid) {
                handlers = ctx.onMessage;
            } else {
                var chan = ctx.channels[msg[3]];
                if (!chan) {
                    console.log("message to non-existant chan " + JSON.stringify(msg));
                    return;
                }
                handlers = chan._.onMessage;
            }
            handlers.forEach(function (h) {
                try {
                    h(msg[4], msg[1]);
                } catch (e) {
                    console.error(e);
                }
            });
        }

        if (msg[2] === 'LEAVE') {
            var _chan = ctx.channels[msg[3]];
            if (!_chan) {
                console.log("leaving non-existant chan " + JSON.stringify(msg));
                return;
            }
            _chan._.onLeave.forEach(function (h) {
                try {
                    h(msg[1], msg[4]);
                } catch (e) {
                    console.log(e.stack);
                }
            });
        }

        if (msg[2] === 'JOIN') {
            var _chan2 = ctx.channels[msg[3]];
            if (!_chan2) {
                console.log("ERROR: join to non-existant chan " + JSON.stringify(msg));
                return;
            }
            // have we yet fully joined the chan?
            var synced = _chan2._.members.indexOf(ctx.uid) !== -1;
            _chan2._.members.push(msg[1]);
            if (!synced && msg[1] === ctx.uid) {
                // sync the channel join event
                _chan2.myID = ctx.uid;
                _chan2._.resolve(_chan2);
            }
            if (synced) {
                _chan2._.onJoin.forEach(function (h) {
                    try {
                        h(msg[1]);
                    } catch (e) {
                        console.log(e.stack);
                    }
                });
            }
        }
    };

    var splitUrl = function (url) {
        var arr = /^((?:wss?:\/\/)?(?:www\.)?(?:[^\/\n]+))(\/?.+)/.exec(url);
        return {
            url: arr[0],
            base: arr[1],
            path: arr[2]
        }
    };

    var connect = function connect(websocketURL, makeWebsocket) {
        makeWebsocket = makeWebsocket || function (url) {
            var urlObj = splitUrl(url);
            return io(urlObj.base, {
                path: urlObj.path,
                //transports: ['websocket']
            });
        };
        var ctx = {
            ws: null,
            seq: 1,
            uid: null,
            network: null,
            channels: {},
            onMessage: [],
            onDisconnect: [],
            onReconnect: [],
            timeouts: [],
            requests: {},
            pingInterval: null,

            timeOfLastPingSent: -1,
            timeOfLastPingReceived: -1,
            lastObservedLag: 0,
            pingOutstanding: 0
        };
        ctx.network = mkNetwork(ctx);

        // For the returned promise, after the promise is resolved/rejected these will be NOFUNC'd.
        var promiseResolve = NOFUNC;
        var promiseReject = NOFUNC;

        var connectWs = function () {
            var ws = ctx.ws = makeWebsocket(websocketURL);
            ctx.timeOfLastPingSent = ctx.timeOfLastPingReceived = now();
            ws.on('message', function (msg) { return onMessage(ctx, {data:msg}); });
            ws.on('disconnect', function (reason) {
                if (reason === 'io server disconnect') {
                    // the disconnection was initiated by the server, you need to reconnect manually
                    ws.connect();
                // else the socket will automatically try to reconnect
                } else if (isIntentionallyLeaving) {
                    // this reason string is matched in other code so don't change it.
                    reason = "network.disconnect() called";
                    isIntentionallyLeaving = false;
                }

                clearInterval(ctx.pingInterval);
                ctx.timeouts.forEach(clearTimeout);
                if (ctx.uid) {
                    ctx.uid = null;
                    ctx.onDisconnect.forEach(function (h) {
                        try {
                            h(reason);
                        } catch (e) {
                            console.log(e.stack);
                        }
                    });
                }
                // Try to reconnect...
                //setTimeoutX(ctx, connectWs, (ctx.uid) ? 0 : RECONNECT_LOOP_CYCLE);
            });
            ws.on('connect', function () {
                setTimeoutX(ctx, function () {
                    if (ctx.uid) { return; }
                    promiseReject({ type: 'TIMEOUT', message: 'waited ' + REQUEST_TIMEOUT + 'ms' });
                    promiseResolve = promiseReject = NOFUNC;
                    closeWebsocket(ctx);
                }, REQUEST_TIMEOUT);
            });
            ctx.ws._onident = function () {
                // This is to use the time of IDENT minus the connect time to guess a ping reception
                ctx.timeOfLastPingReceived = now();
                ctx.lastObservedLag = now() - ctx.timeOfLastPingSent;

                if (promiseResolve !== NOFUNC) {
                    promiseResolve(ctx.network);
                    promiseResolve = promiseReject = NOFUNC;
                } else {
                    ctx.channels = {};
                    ctx.requests = {};
                    ctx.pingOutstanding = 0;
                    ctx.onReconnect.forEach(function (h) {
                       try {
                            h(ctx.uid);
                        } catch (e) {
                            console.log(e.stack);
                        }
                    });
                }
            }
        };

        return new Promise(function (resolve, reject) {
            promiseResolve = resolve;
            promiseReject = reject;
            connectWs();
        });
    };

    return { connect: connect };
});
<|endoftext|>


<commit_before>var quicktest = function(svg, data, Plottable) {
          var dataseries1 = new Plottable.DataSource(data[0].slice(0, 20));
          dataseries1.metadata({name: "series1"});
          var dataseries2 = new Plottable.DataSource(data[1].slice(0, 20));
          dataseries2.metadata({name: "series2"});
          colorScale1 = new Plottable.Scale.Color("10");
          colorScale1.domain(["series1", "series2"]);

          //Axis
          var xScale = new Plottable.Scale.Linear();
          var yScale = new Plottable.Scale.Linear();
          var xAxis = new Plottable.Axis.XAxis(xScale, "bottom");
          var yAxis = new Plottable.Axis.YAxis(yScale, "left");

          var colorProjector = function(d, i, m) {
             return colorScale1.scale(m.name);
          };

          renderAreaD1 = new Plottable.Plot.Scatter(dataseries1, xScale, yScale);
          renderAreaD2 = new Plottable.Plot.Line(dataseries2, xScale, yScale);
          renderAreaD1.project("fill", colorProjector);
          renderAreaD2.project("stroke", colorProjector);
          var renderAreas = renderAreaD1.merge(renderAreaD2);

          title1 = new Plottable.Component.TitleLabel( "Two Data Series", "horizontal");
          legend1 = new Plottable.Component.Legend(colorScale1);
          var titleTable = new Plottable.Component.Table().addComponent(0,0, title1)
                                                .addComponent(0,1, legend1);

          basicTable = new Plottable.Component.Table().addComponent(0,2, titleTable)
                      .addComponent(1, 1, yAxis)
                      .addComponent(1, 2, renderAreas)
                      .addComponent(2, 2, xAxis)

          basicTable.renderTo(svg);

}

function button1Callback() {
    title1.setText("");
}
function button2Callback() {
    title1.setText("tiny");
}
function button3Callback() {
    title1.setText("abcdefghij klmnopqrs tuvwxyz ABCDEF GHIJK LMNOP QRSTUV WXYZ");
}
function button4Callback() {
    colorScale1.domain([]);
}
function button5Callback() {
    colorScale1.domain(["series1", "series2"]);
}
function button6Callback() {
    colorScale1.domain(["series1", "series2", "apples", "oranges", "bananas", "grapes"]);
}

quicktest.quicktestName = "layout-invalidate";
quicktest.categories = ["layout", "legend", "scatter", "line", "title"];
quicktests.push(quicktest);
<commit_msg>Update legend_basic.js<commit_after>var quicktest = function(svg, data, Plottable) {
          var dataseries1 = new Plottable.DataSource(data[0].slice(0, 20));
          dataseries1.metadata({name: "series1"});
          var dataseries2 = new Plottable.DataSource(data[1].slice(0, 20));
          dataseries2.metadata({name: "series2"});
          colorScale1 = new Plottable.Scale.Color("10");
          colorScale1.domain(["series1", "series2"]);

          //Axis
          var xScale = new Plottable.Scale.Linear();
          var yScale = new Plottable.Scale.Linear();
          var xAxis = new Plottable.Axis.XAxis(xScale, "bottom");
          var yAxis = new Plottable.Axis.YAxis(yScale, "left");

          var colorProjector = function(d, i, m) {
             return colorScale1.scale(m.name);
          };

          renderAreaD1 = new Plottable.Plot.Scatter(dataseries1, xScale, yScale);
          renderAreaD2 = new Plottable.Plot.Line(dataseries2, xScale, yScale);
          renderAreaD1.project("fill", colorProjector);
          renderAreaD2.project("stroke", colorProjector);
          var renderAreas = renderAreaD1.merge(renderAreaD2);

          title1 = new Plottable.Component.TitleLabel( "Two Data Series", "horizontal");
          legend1 = new Plottable.Component.Legend(colorScale1);
          var titleTable = new Plottable.Component.Table().addComponent(0,0, title1)
                                                .addComponent(0,1, legend1);

          basicTable = new Plottable.Component.Table().addComponent(0,2, titleTable)
                      .addComponent(1, 1, yAxis)
                      .addComponent(1, 2, renderAreas)
                      .addComponent(2, 2, xAxis)

          basicTable.renderTo(svg);

}

function button1Callback() {
    title1.setText("");
}
function button2Callback() {
    title1.setText("tiny");
}
function button3Callback() {
    title1.setText("abcdefghij klmnopqrs tuvwxyz ABCDEF GHIJK LMNOP QRSTUV WXYZ");
}
function button4Callback() {
    colorScale1.domain([]);
}
function button5Callback() {
    colorScale1.domain(["series1", "series2"]);
}
function button6Callback() {
    colorScale1.domain(["series1", "series2", "apples", "oranges", "bananas", "grapes"]);
}

quicktest.quicktestName = "legend_basic";
quicktests.push(quicktest);
<|endoftext|>