<commit_before>(function() {
  /**
   * Interface: cachinglayer
   *
   * This module defines functions that are mixed into remoteStorage.local when
   * it is instantiated (currently one of indexeddb.js, localstorage.js, or
   * inmemorystorage.js).
   *
   * All remoteStorage.local implementations should therefore implement
   * this.getNodes, this.setNodes, and this.forAllNodes. The rest is blended in
   * here to create a GPD (get/put/delete) interface which the BaseClient can
   * talk to.
   */

  function isFolder(path) {
    return path.substr(-1) === '/';
  }

  function isDocument(path) {
    return path.substr(-1) !== '/';
  }

  function deepClone(obj) {
    if (obj === undefined) {
      return undefined;
    } else {
      return JSON.parse(JSON.stringify(obj));
    }
  }

  function equal(obj1, obj2) {
    return JSON.stringify(obj1) === JSON.stringify(obj2);
  }

  function getLatest(node) {
    if (typeof(node) !== 'object' || typeof(node.path) !== 'string') {
      return;
    }
    if (isFolder(node.path)) {
      if (node.local && node.local.itemsMap) {
        return node.local;
      }
      if (node.common && node.common.itemsMap) {
        return node.common;
      }
    } else {
      if (node.local && node.local.body && node.local.contentType) {
        return node.local;
      }
      if (node.common && node.common.body && node.common.contentType) {
        return node.common;
      }
      // Migration code! Once all apps use at least this version of the lib, we
      // can publish clean-up code that migrates over any old-format data, and
      // stop supporting it. For now, new apps will support data in both
      // formats, thanks to this:
      if (node.body && node.contentType) {
        return {
          body: node.body,
          contentType: node.contentType
        };
      }
    }
  }

  function isOutdated(node, maxAge) {
    return !node || !node.timestamp ||
           ((new Date().getTime()) - node.timestamp > maxAge);
  }

  function pathsFromRoot(path) {
    var paths = [path];
    var parts = path.replace(/\/$/, '').split('/');

    while (parts.length > 1) {
      parts.pop();
      paths.push(parts.join('/')+'/');
    }
    return paths;
  }

  function makeNode(path, timestamp) {
    var node = { path: path, common: { timestamp: timestamp } };

    if (isFolder(path)) {
      node.common.itemsMap = {};
    }
    return node;
  }

  function updateFolderNodeWithItemName(node, itemName) {
    if (!node.common) {
      node.common = {
        timestamp: now,
        itemsMap: {}
      };
    }
    if (!node.common.itemsMap) {
      node.common.itemsMap = {};
    }
    if (!node.local) {
      node.local = deepClone(node.common);
    }
    if (!node.local.itemsMap) {
      node.local.itemsMap = node.common.itemsMap;
    }
    node.local.itemsMap[itemName] = true;

    return node;
  }

  var methods = {

    get: function(path, maxAge) {
      var promise = promising();

      this.getNodes([path]).then(function(objs) {
        var node = getLatest(objs[path]);
        if ((typeof(maxAge) === 'number') && isOutdated(node, maxAge)) {
          remoteStorage.sync.queueGetRequest(path, promise);
        }

        if (node) {
          promise.fulfill(200, node.body || node.itemsMap, node.contentType);
        } else {
          promise.fulfill(404);
        }
      }.bind(this), function(err) {
        promise.reject(err);
      }.bind(this));

      return promise;
    },

    put: function(path, body, contentType) {
      var paths = pathsFromRoot(path);
      var now = new Date().getTime();

      return this._updateNodes(paths, function(nodes) {
        try {
          for (var i=0; i<paths.length; i++) {
            var path = paths[i];
            var node = nodes[path];
            var previous;

            if (!node) {
              nodes[path] = node = makeNode(path, now);
            }

            // Document
            if (i === 0) {
              previous = getLatest(node);
              node.local = {
                body:                body,
                contentType:         contentType,
                timestamp:           now,
                previousBody:        (previous ? previous.body : undefined),
                previousContentType: (previous ? previous.contentType : undefined),
              };
            }
            // Folder
            else {
              var itemName = paths[i-1].substring(path.length);
              node = updateFolderNodeWithItemName(node, itemName);
            }
          }
          return nodes;
        } catch(e) {
          RemoteStorage.log('Error during PUT', nodes, i, e);
          throw e;
        }
      });
    },

    delete: function(path) {
      var paths = pathsFromRoot(path);

      return this._updateNodes(paths, function(nodes) {
        var now = new Date().getTime();

        for (var i=0; i<paths.length; i++) {
          var path = paths[i];
          var node = nodes[path];

          if (!node) {
            throw new Error('Cannot delete non-existing node '+path);
          }

          // Document
          if (i === 0) {
            // TODO should body better be undefined?
            node.local = { body: false, timestamp: now };
          }
          // Folder
          else {
            if (!node.local) {
              node.local = deepClone(node.common);
            }
            var itemName = paths[i-1].substring(path.length);
            delete node.local.itemsMap[itemName];

            if (Object.getOwnPropertyNames(node.local.itemsMap).length > 0) {
              // This folder still contains other items, don't remove any further ancestors
              break;
            }
          }
        }
        return nodes;
      });
    },

    flush: function(path) {
      return this._getAllDescendentPaths(path).then(function(paths) {
        return this.getNodes(paths);
      }.bind(this)).then(function(objs) {
        var i;
        for (i in objs) {
          if (objs[i] && objs[i].common && objs[i].local) {
            this._emit('change', {
              path: objs[i].path,
              origin: 'local',
              oldValue: (objs[i].local.body === false ? undefined : objs[i].local.body),
              newValue: (objs[i].common.body === false ? undefined : objs[i].common.body)
            });
          }
          objs[i] = undefined;
        }
        return this.setNodes(objs);
      }.bind(this));
    },

    fireInitial: function() {
      this.forAllNodes(function(node) {
        var latest;
        if (isDocument(node.path)) {
          latest = getLatest(node);
          if (latest) {
            this._emit('change', {
              path: node.path,
              origin: 'local',
              oldValue: undefined,
              oldContentType: undefined,
              newValue: latest.body,
              newContentType: latest.contentType
            });
          }
        }
      }.bind(this));
    },

    /**
     * Method: onDiff
     *
     * Set an event handler for ?
     */
    onDiff: function(diffHandler) {
      this.diffHandler = diffHandler;
    },

    migrate: function(node) {
      if (typeof(node) === 'object' && !node.common) {
        node.common = {};
        if (typeof(node.path) === 'string') {
          if (node.path.substr(-1) === '/' && typeof(node.body) === 'object') {
            node.common.itemsMap = node.body;
          }
        } else {
          //save legacy content of document node as local version
          if (!node.local) {
            node.local = {};
          }
          node.local.body = node.body;
          node.local.contentType = node.contentType;
        }
      }
      return node;
    },

    _updateNodes: function(paths, cb) {
      var self = this;

      return this.getNodes(paths).then(function(nodes) {
        var existingNodes = deepClone(nodes);
        var changeEvents = [];

        nodes = cb(nodes);

        for (var path in nodes) {
          if (equal(nodes[path], existingNodes[path])) {
            delete nodes[path];
          }
          else if(isDocument(path)) {
            changeEvents.push({
              path:           path,
              origin:         'window',
              oldValue:       nodes[path].local.previousBody,
              newValue:       nodes[path].local.body,
              oldContentType: nodes[path].local.previousContentType,
              newContentType: nodes[path].local.contentType
            });
            delete nodes[path].local.previousBody;
            delete nodes[path].local.previousContentType;
          }
        }

        return self.setNodes(nodes).then(function() {
          self._emitChangeEvents(changeEvents);
          return 200;
        });
      },
      function(err) {
        throw(err);
      });
    },

    _emitChangeEvents: function(events) {
      for (var i=0; i<events.length; i++) {
        this._emit('change', events[i]);
        if (this.diffHandler) {
          this.diffHandler(events[i].path);
        }
      }
    },

    _getAllDescendentPaths: function(path) {
      if (isFolder(path)) {
        return this.getNodes([path]).then(function(objs) {
          var i, pending=0, allPaths = [path], latest = getLatest(objs[path]), promise = promising();
          for (i in latest.itemsMap) {
            pending++;
            var subPromise = this._getAllDescendentPaths(path+i);
            subPromise.then(function(paths) {
              var j;
              pending--;
              for (j=0; j<paths.length; j++) {
                allPaths.push(paths[j]);
              }
              if (pending === 0) {
                promise.fulfill(allPaths);
              }
            });
          }
          return promise;
        }.bind(this));
      } else {
        return promising().fulfill([path]);
      }
    },

    _getInternals: function() {
      return {
        isFolder: isFolder,
        isDocument: isDocument,
        deepClone: deepClone,
        equal: equal,
        getLatest: getLatest,
        pathsFromRoot: pathsFromRoot,
        makeNode: makeNode
      };
    }
  };

  /**
   * Function: cachingLayer
   *
   * Mixes common caching layer functionality into an object.
   *
   * The first parameter is always the object to be extended.
   *
   * Example:
   *   (start code)
   *   var MyConstructor = function() {
   *     cachingLayer(this);
   *   };
   *   (end code)
   */
  RemoteStorage.cachingLayer = function(object) {
    for (var key in methods) {
      object[key] = methods[key];
    }
  };
})();
<commit_msg>Use more expressive naming in cachinglayer flush method<commit_after>(function() {
  /**
   * Interface: cachinglayer
   *
   * This module defines functions that are mixed into remoteStorage.local when
   * it is instantiated (currently one of indexeddb.js, localstorage.js, or
   * inmemorystorage.js).
   *
   * All remoteStorage.local implementations should therefore implement
   * this.getNodes, this.setNodes, and this.forAllNodes. The rest is blended in
   * here to create a GPD (get/put/delete) interface which the BaseClient can
   * talk to.
   */

  function isFolder(path) {
    return path.substr(-1) === '/';
  }

  function isDocument(path) {
    return path.substr(-1) !== '/';
  }

  function deepClone(obj) {
    if (obj === undefined) {
      return undefined;
    } else {
      return JSON.parse(JSON.stringify(obj));
    }
  }

  function equal(obj1, obj2) {
    return JSON.stringify(obj1) === JSON.stringify(obj2);
  }

  function getLatest(node) {
    if (typeof(node) !== 'object' || typeof(node.path) !== 'string') {
      return;
    }
    if (isFolder(node.path)) {
      if (node.local && node.local.itemsMap) {
        return node.local;
      }
      if (node.common && node.common.itemsMap) {
        return node.common;
      }
    } else {
      if (node.local && node.local.body && node.local.contentType) {
        return node.local;
      }
      if (node.common && node.common.body && node.common.contentType) {
        return node.common;
      }
      // Migration code! Once all apps use at least this version of the lib, we
      // can publish clean-up code that migrates over any old-format data, and
      // stop supporting it. For now, new apps will support data in both
      // formats, thanks to this:
      if (node.body && node.contentType) {
        return {
          body: node.body,
          contentType: node.contentType
        };
      }
    }
  }

  function isOutdated(node, maxAge) {
    return !node || !node.timestamp ||
           ((new Date().getTime()) - node.timestamp > maxAge);
  }

  function pathsFromRoot(path) {
    var paths = [path];
    var parts = path.replace(/\/$/, '').split('/');

    while (parts.length > 1) {
      parts.pop();
      paths.push(parts.join('/')+'/');
    }
    return paths;
  }

  function makeNode(path, timestamp) {
    var node = { path: path, common: { timestamp: timestamp } };

    if (isFolder(path)) {
      node.common.itemsMap = {};
    }
    return node;
  }

  function updateFolderNodeWithItemName(node, itemName) {
    if (!node.common) {
      node.common = {
        timestamp: now,
        itemsMap: {}
      };
    }
    if (!node.common.itemsMap) {
      node.common.itemsMap = {};
    }
    if (!node.local) {
      node.local = deepClone(node.common);
    }
    if (!node.local.itemsMap) {
      node.local.itemsMap = node.common.itemsMap;
    }
    node.local.itemsMap[itemName] = true;

    return node;
  }

  var methods = {

    get: function(path, maxAge) {
      var promise = promising();

      this.getNodes([path]).then(function(objs) {
        var node = getLatest(objs[path]);
        if ((typeof(maxAge) === 'number') && isOutdated(node, maxAge)) {
          remoteStorage.sync.queueGetRequest(path, promise);
        }

        if (node) {
          promise.fulfill(200, node.body || node.itemsMap, node.contentType);
        } else {
          promise.fulfill(404);
        }
      }.bind(this), function(err) {
        promise.reject(err);
      }.bind(this));

      return promise;
    },

    put: function(path, body, contentType) {
      var paths = pathsFromRoot(path);
      var now = new Date().getTime();

      return this._updateNodes(paths, function(nodes) {
        try {
          for (var i=0; i<paths.length; i++) {
            var path = paths[i];
            var node = nodes[path];
            var previous;

            if (!node) {
              nodes[path] = node = makeNode(path, now);
            }

            // Document
            if (i === 0) {
              previous = getLatest(node);
              node.local = {
                body:                body,
                contentType:         contentType,
                timestamp:           now,
                previousBody:        (previous ? previous.body : undefined),
                previousContentType: (previous ? previous.contentType : undefined),
              };
            }
            // Folder
            else {
              var itemName = paths[i-1].substring(path.length);
              node = updateFolderNodeWithItemName(node, itemName);
            }
          }
          return nodes;
        } catch(e) {
          RemoteStorage.log('Error during PUT', nodes, i, e);
          throw e;
        }
      });
    },

    delete: function(path) {
      var paths = pathsFromRoot(path);

      return this._updateNodes(paths, function(nodes) {
        var now = new Date().getTime();

        for (var i=0; i<paths.length; i++) {
          var path = paths[i];
          var node = nodes[path];

          if (!node) {
            throw new Error('Cannot delete non-existing node '+path);
          }

          // Document
          if (i === 0) {
            // TODO should body better be undefined?
            node.local = { body: false, timestamp: now };
          }
          // Folder
          else {
            if (!node.local) {
              node.local = deepClone(node.common);
            }
            var itemName = paths[i-1].substring(path.length);
            delete node.local.itemsMap[itemName];

            if (Object.getOwnPropertyNames(node.local.itemsMap).length > 0) {
              // This folder still contains other items, don't remove any further ancestors
              break;
            }
          }
        }
        return nodes;
      });
    },

    flush: function(path) {
      return this._getAllDescendentPaths(path).then(function(paths) {
        return this.getNodes(paths);
      }.bind(this)).then(function(nodes) {
        for (var path in nodes) {
          var node = nodes[path];
          if (node && node.common && node.local) {
            this._emit('change', {
              path:     node.path,
              origin:   'local',
              oldValue: (node.local.body === false ? undefined : node.local.body),
              newValue: (node.common.body === false ? undefined : node.common.body)
            });
          }
          nodes[path] = undefined;
        }
        return this.setNodes(nodes);
      }.bind(this));
    },

    fireInitial: function() {
      this.forAllNodes(function(node) {
        var latest;
        if (isDocument(node.path)) {
          latest = getLatest(node);
          if (latest) {
            this._emit('change', {
              path:           node.path,
              origin:         'local',
              oldValue:       undefined,
              oldContentType: undefined,
              newValue:       latest.body,
              newContentType: latest.contentType
            });
          }
        }
      }.bind(this));
    },

    /**
     * Method: onDiff
     *
     * Set an event handler for ?
     */
    onDiff: function(diffHandler) {
      this.diffHandler = diffHandler;
    },

    migrate: function(node) {
      if (typeof(node) === 'object' && !node.common) {
        node.common = {};
        if (typeof(node.path) === 'string') {
          if (node.path.substr(-1) === '/' && typeof(node.body) === 'object') {
            node.common.itemsMap = node.body;
          }
        } else {
          //save legacy content of document node as local version
          if (!node.local) {
            node.local = {};
          }
          node.local.body = node.body;
          node.local.contentType = node.contentType;
        }
      }
      return node;
    },

    _updateNodes: function(paths, cb) {
      var self = this;

      return this.getNodes(paths).then(function(nodes) {
        var existingNodes = deepClone(nodes);
        var changeEvents = [];

        nodes = cb(nodes);

        for (var path in nodes) {
          if (equal(nodes[path], existingNodes[path])) {
            delete nodes[path];
          }
          else if(isDocument(path)) {
            changeEvents.push({
              path:           path,
              origin:         'window',
              oldValue:       nodes[path].local.previousBody,
              newValue:       nodes[path].local.body,
              oldContentType: nodes[path].local.previousContentType,
              newContentType: nodes[path].local.contentType
            });
            delete nodes[path].local.previousBody;
            delete nodes[path].local.previousContentType;
          }
        }

        return self.setNodes(nodes).then(function() {
          self._emitChangeEvents(changeEvents);
          return 200;
        });
      },
      function(err) {
        throw(err);
      });
    },

    _emitChangeEvents: function(events) {
      for (var i=0; i<events.length; i++) {
        this._emit('change', events[i]);
        if (this.diffHandler) {
          this.diffHandler(events[i].path);
        }
      }
    },

    _getAllDescendentPaths: function(path) {
      if (isFolder(path)) {
        return this.getNodes([path]).then(function(objs) {
          var i, pending=0, allPaths = [path], latest = getLatest(objs[path]), promise = promising();
          for (i in latest.itemsMap) {
            pending++;
            var subPromise = this._getAllDescendentPaths(path+i);
            subPromise.then(function(paths) {
              var j;
              pending--;
              for (j=0; j<paths.length; j++) {
                allPaths.push(paths[j]);
              }
              if (pending === 0) {
                promise.fulfill(allPaths);
              }
            });
          }
          return promise;
        }.bind(this));
      } else {
        return promising().fulfill([path]);
      }
    },

    _getInternals: function() {
      return {
        isFolder: isFolder,
        isDocument: isDocument,
        deepClone: deepClone,
        equal: equal,
        getLatest: getLatest,
        pathsFromRoot: pathsFromRoot,
        makeNode: makeNode
      };
    }
  };

  /**
   * Function: cachingLayer
   *
   * Mixes common caching layer functionality into an object.
   *
   * The first parameter is always the object to be extended.
   *
   * Example:
   *   (start code)
   *   var MyConstructor = function() {
   *     cachingLayer(this);
   *   };
   *   (end code)
   */
  RemoteStorage.cachingLayer = function(object) {
    for (var key in methods) {
      object[key] = methods[key];
    }
  };
})();
<|endoftext|>


<commit_before>#!/usr/bin/env node
const semanticRelease = require('semantic-release')
const {spawnSync} = require('child_process')
const {WritableStreamBuffer} = require('stream-buffers')

const branch = spawnSync('git', ['symbolic-ref', '--short', 'HEAD'], {encoding: 'utf8'}).stdout.trim()
const options = {branch, dryRun: true}
console.warn(`Running semantic-release with options: ${JSON.stringify(options)}...`)
semanticRelease(options, {
  stdout: new WritableStreamBuffer(),
  stderr: new WritableStreamBuffer()
}).then(result => {
  const {
    nextRelease: {version, notes}
  } = result
  console.warn(`Release ${version} notes:\n`)
  console.log(notes)
})
<commit_msg>get branch name from GITHUB_REF<commit_after>#!/usr/bin/env node
const semanticRelease = require('semantic-release')
const {spawnSync} = require('child_process')
const {WritableStreamBuffer} = require('stream-buffers')

const branch = getBranch()
const options = {branch, dryRun: true}

console.warn(`Running semantic-release with options: ${JSON.stringify(options)}...`)

semanticRelease(options, {
  stdout: new WritableStreamBuffer(),
  stderr: new WritableStreamBuffer()
})
  .then(result => {
    const {
      nextRelease: {version, notes}
    } = result
    console.warn(`Release ${version} notes:\n`)
    console.log(notes)
  })
  .catch(error => {
    console.error(error.message)
    process.exitCode = 1
  })

function getBranch() {
  const {GITHUB_REF} = process.env
  if (GITHUB_REF) {
    return GITHUB_REF.replace(/^refs\/heads\//, '')
  } else {
    return spawnSync('git', ['symbolic-ref', '--short', 'HEAD'], {encoding: 'utf8'}).stdout.trim()
  }
}
<|endoftext|>


<commit_before>var EyeballControllersTestTesters = function() {

    var YSLOW = require('yslow').YSLOW;
    var jsdom = require('jsdom');

    var testCfg = require('../../conf/test');
    var grader = require('./grader');
    var yslowOverride = require("./yslowOverride");
    var validator = require("./validator");

    function getDomMonster(page,cb) {
        var dm = page.EYEBALLTEST.dommonster;

        var i;
        for(i in dm.stats) {
            if(dm.stats.hasOwnProperty(i) && typeof dm.stats[i] === "string") {
                // make string values numbers by removing units
                dm.stats[i] = dm.stats[i].replace(/[A-z]|%|\s/g,'');
            }
        }
        cb(dm);
        return dm;
    }

    function runYslow(har,cb) {
        return cb(yslowOverride.yslowOverrideGetResults(YSLOW.harImporter.run(jsdom.jsdom(), har, 'ydefault').context, 'grade,stats'));
    }

    function addEyeballMetrics(record) {
        record.metrics.eyeball = {
            url : record.url,
            timestamp: new Date(),
            build : record.build,
            tag : record.tag,
            data : {},
            grades : {},
            tool : 'eyeball'
        };

        var eyeballScore = 0, score, eyeballMetrics, metrics, metric, grade, eyeballGrade;

        for(score in testCfg.eyeballScoring) {
            if(testCfg.eyeballScoring.hasOwnProperty(score)) {
                eyeballScore = 0;
                metrics = 0;
                eyeballMetrics = testCfg.eyeballScoring[score].metrics;
                for(metric in eyeballMetrics) {
                    if(eyeballMetrics.hasOwnProperty(metric) && record.metrics.hasOwnProperty(metric)) {
                        grade = record.metrics[metric].grades[eyeballMetrics[metric].metric];
                        if(grader.getValue(grade,'points')) {
                            eyeballScore += grader.getValue(grade,'points');
                            metrics += eyeballMetrics[metric].influence;
                        }
                    }
                }

                eyeballScore = Math.floor(eyeballScore/metrics);
                eyeballGrade = grader.getGrades(eyeballScore,'points');
                record.metrics.eyeball.data[score] = eyeballScore;
                record.metrics.eyeball.grades[score] = eyeballGrade;
            }

        }
        return record;
    }

    function runValidator(data,cb) {
        validator.validate(data,cb);
    }

    function aria(file,cb) {
        var AriaLinter = require('arialinter');

        AriaLinter.initialize(file, function() {
            AriaLinter.evaluate();
            var warnings = 0;
            var errors = 0;
            var val = AriaLinter.getReport('json');
            val = val.replace(/\\/g,'').replace(/\r?\n|\r/g,'').replace(/\t/g,'');
            val = val.replace(/\"element\"\:\s"(.*?)\"\s\}/g,function(v,$1) {
                if($1.length > 500) {
                    $1 = $1.substring(0,500) + '...';
                }
                return '"element":"'+$1.replace(/"/g,'\\"') + '" }';
            });
            val = JSON.parse(val);

            var i = 0;
            for (i=val.errors.length-1; i>=0; i--) {
                if(val.errors[i].type === "Error") {
                    errors += 1;
                } else if(val.errors[i].type === "Info") {
                    warnings += 1;
                }
            }
            val.info = {
                errors : errors,
                warnings : warnings
            };
            cb(val);
        });
    }

    return {
        eyeball : addEyeballMetrics,
        dommonster : getDomMonster,
        yslow : runYslow,
        validator : runValidator,
        aria : aria
    };

};

module.exports = EyeballControllersTestTesters();<commit_msg>improve regex for sanitising json and limit the amount of data returned<commit_after>var EyeballControllersTestTesters = function() {

    var YSLOW = require('yslow').YSLOW;
    var jsdom = require('jsdom');

    var testCfg = require('../../conf/test');
    var grader = require('./grader');
    var yslowOverride = require("./yslowOverride");
    var validator = require("./validator");

    function getDomMonster(page,cb) {
        var dm = page.EYEBALLTEST.dommonster;

        var i;
        for(i in dm.stats) {
            if(dm.stats.hasOwnProperty(i) && typeof dm.stats[i] === "string") {
                // make string values numbers by removing units
                dm.stats[i] = dm.stats[i].replace(/[A-z]|%|\s/g,'');
            }
        }
        cb(dm);
        return dm;
    }

    function runYslow(har,cb) {
        return cb(yslowOverride.yslowOverrideGetResults(YSLOW.harImporter.run(jsdom.jsdom(), har, 'ydefault').context, 'grade,stats'));
    }

    function addEyeballMetrics(record) {
        record.metrics.eyeball = {
            url : record.url,
            timestamp: new Date(),
            build : record.build,
            tag : record.tag,
            data : {},
            grades : {},
            tool : 'eyeball'
        };

        var eyeballScore = 0, score, eyeballMetrics, metrics, metric, grade, eyeballGrade;

        for(score in testCfg.eyeballScoring) {
            if(testCfg.eyeballScoring.hasOwnProperty(score)) {
                eyeballScore = 0;
                metrics = 0;
                eyeballMetrics = testCfg.eyeballScoring[score].metrics;
                for(metric in eyeballMetrics) {
                    if(eyeballMetrics.hasOwnProperty(metric) && record.metrics.hasOwnProperty(metric)) {
                        grade = record.metrics[metric].grades[eyeballMetrics[metric].metric];
                        if(grader.getValue(grade,'points')) {
                            eyeballScore += grader.getValue(grade,'points');
                            metrics += eyeballMetrics[metric].influence;
                        }
                    }
                }

                eyeballScore = Math.floor(eyeballScore/metrics);
                eyeballGrade = grader.getGrades(eyeballScore,'points');
                record.metrics.eyeball.data[score] = eyeballScore;
                record.metrics.eyeball.grades[score] = eyeballGrade;
            }

        }
        return record;
    }

    function runValidator(data,cb) {
        validator.validate(data,cb);
    }

    function aria(file,cb) {
        var AriaLinter = require('arialinter');

        AriaLinter.initialize(file, function() {
            AriaLinter.evaluate();
            var warnings = 0;
            var errors = 0;
            var val = AriaLinter.getReport('json');
            val = val.replace(/\\/g,'').replace(/\r?\n|\r/g,'').replace(/\t/g,'');
            val = val.replace(/\"element\"\:\s"(.*?)\"\s\}/g,function(v,$1) {
                if($1.length > 500) {
                    $1 = $1.substring(0,500) + '...';
                }
                return '"element":"'+$1.replace(/"/g,'\\"') + '" }';
            });
            val = JSON.parse(val);

            //console.log(AriaLinter.getReport('text'))

            var i = 0;
            for (i=val.errors.length-1; i>=0; i--) {
                if(val.errors[i].type === "Error") {
                    errors += 1;
                } else if(val.errors[i].type === "Info") {
                    warnings += 1;
                }
            }
            val.info = {
                errors : errors,
                warnings : warnings
            };
            cb(val);
        });
    }

    return {
        eyeball : addEyeballMetrics,
        dommonster : getDomMonster,
        yslow : runYslow,
        validator : runValidator,
        aria : aria
    };

};

module.exports = EyeballControllersTestTesters();<|endoftext|>


<commit_before>var _ = require('underscore');
var mongoose = require('mongoose');
var pkg = require('../package');
var defaults = require('../configs');

exports.Schema = mongoose.Schema;

exports.connect = function(db) {
  // if database is a mongodb:// URI, init by this URI.
  if (exports.isMongodbUri(db)) return mongoose.createConnection(db);
  var database = _.isObject(db) ? db : {};
  var host = database.host || 'localhost';
  var dbname = database.name || pkg.name;
  var options = database.options || {};
  var port = _.isNumber(database.port) ? parseInt(database.port, 10) : 27017;
  return mongoose.createConnection(host, dbname, port, options);
}

exports.parseDbname = function(dbstr) {
  if (!dbstr) return defaults.database.name;
  var params = dbstr.split('/');
  // check if last param is '' cause '/' is the last letter.
  var dburi = params[params.length - 1] ?
  params[params.length - 1] : params[params.length - 2];
  // escape from '?'
  var questionDevider = dburi.indexOf('?');
  if (questionDevider === -1) return dburi;
  return dburi.substr(0, questionDevider);
}

exports.isMongodbUri = function(str) {
  return str && _.isString(str) && str.indexOf('mongodb://') === 0;
}<commit_msg>0.5: Format codes<commit_after>var _ = require('underscore');
var mongoose = require('mongoose');
var pkg = require('../package');
var defaults = require('../configs');

exports.connect = connect;
exports.Schema = mongoose.Schema;
exports.parseDbname = parseDbname;
exports.isMongodbUri = isMongodbUri;

function connect(db) {
  // if database is a `mongodb://` URI, init by this URI.
  if (exports.isMongodbUri(db))
    return mongoose.createConnection(db);

  var database = _.isObject(db) ? db : {};
  var host = database.host || 'localhost';
  var dbname = database.name || pkg.name;
  var options = database.options || {};
  var port = _.isNumber(database.port) ? parseInt(database.port, 10) : 27017;

  return mongoose.createConnection(host, dbname, port, options);
}

function parseDbname(dbstr) {
  if (!dbstr)
    return defaults.database.name;

  var params = dbstr.split('/');

  // check if last param is '' cause '/' is the last letter.
  var dburi = params[params.length - 1] ?
  params[params.length - 1] : params[params.length - 2];

  // escape from '?'
  var questionDevider = dburi.indexOf('?');

  if (questionDevider === -1)
    return dburi;

  return dburi.substr(0, questionDevider);
}

function isMongodbUri(str) {
  return str && _.isString(str) && str.indexOf('mongodb://') === 0;
}
<|endoftext|>


<commit_before>angular.module('MashAcademy')
.controller('DataCtrl', function ($scope, $http, $timeout) {

    $scope.temperatures = {
        "Perth": [
            ["2014-01-01", 10],
            ["2014-01-02", 20],
            ["2014-01-03", 30]
        ]
    };
    $scope.currentTemperature = function (city) {
        return $scope.temperatures[city].reduce(function (m, e) {
            if (m) return m;

            var date = Date.parse(e[0]);

            if (date.getTime() == $scope.sliderValue) {
                return e[1];
            }
        });
    };

    $scope.times = Object.keys($scope.temperatures).map(function (e) {
        return $scope.temperatures[e];
    }).reduce(function (joined, arr) {
        return joined.concat(arr);
    }, []).map(function (element) {
        return Date.parse(element[0]);
    });

    $scope.maxTime = $scope.times.reduce(toMax);
    $scope.minTime = $scope.times.reduce(toMin);

    function toMax(m, e) {
        return m > e ? m : e;
    }
    function toMin(m, e) {
        return m < e ? m : e;
    }
    function dateToMSecs(date) {
        return date.getTime();
    }

    $scope.toDate = function (msecs) {
        var date = new Date();
        date.setTime(msecs);
        return moment(date).format("YYYY MMM DD");
    };

    $scope.$watch('hasControl("Time")', function (hasTime) {
        $timeout(function () {
            $scope.fireResize();
        });
    });
})
.factory('dataService', function ($http) {

    var nameMap = {
        '9021': 'Perth',
        '3003': 'Broome',
        '14015': 'Darwin',
        '31011': 'Cairns',
        '40913': 'Brisbane',
        '66037': 'Sydney',
        '86282': 'Melbourne',
        '23034': 'Adelaide',
        '94029': 'Hobart'
    };

    var rainfall;

    return {
        getRainfall: function () {
            return rainfall = rainfall || $http.get('Data/TotalRain.csv').then(function (response) {
                return response.data;
            }).then(function (csvString) {
                var records = csvString.split('\n');
                var fields = records.shift().split(',');

                var name = 0;
                var year = 1;
                var month = 2;
                var day = 3;
                var rain = 4;

                records = records.map(function (row) { return row.split(','); });
                records = records.map(function (row) {
                    var rowObj = {};
                    rowObj.name = nameMap[row[name]] || 'Unknown id: ' + row[name];
                    rowObj.date = new Date(row[year], row[month], row[day]);
                    rowObj.value = row[rain];
                    return rowObj;
                });

                return records;
            });
        }
    }
});<commit_msg>Ability to fetch rain data.<commit_after>angular.module('MashAcademy')
.controller('DataCtrl', function ($scope, $http, $timeout) {

    $scope.temperatures = {
        "Perth": [
            ["2014-01-01", 10],
            ["2014-01-02", 20],
            ["2014-01-03", 30]
        ]
    };
    $scope.currentTemperature = function (city) {
        return $scope.temperatures[city].reduce(function (m, e) {
            if (m) return m;

            var date = Date.parse(e[0]);

            if (date.getTime() == $scope.sliderValue) {
                return e[1];
            }
        });
    };

    $scope.times = Object.keys($scope.temperatures).map(function (e) {
        return $scope.temperatures[e];
    }).reduce(function (joined, arr) {
        return joined.concat(arr);
    }, []).map(function (element) {
        return Date.parse(element[0]);
    });

    $scope.maxTime = $scope.times.reduce(toMax);
    $scope.minTime = $scope.times.reduce(toMin);

    function toMax(m, e) {
        return m > e ? m : e;
    }
    function toMin(m, e) {
        return m < e ? m : e;
    }
    function dateToMSecs(date) {
        return date.getTime();
    }

    $scope.toDate = function (msecs) {
        var date = new Date();
        date.setTime(msecs);
        return moment(date).format("YYYY MMM DD");
    };

    $scope.$watch('hasControl("Time")', function (hasTime) {
        $timeout(function () {
            $scope.fireResize();
        });
    });
})
.factory('dataService', function ($http) {

    var nameMap = {
        '9021': 'Perth',
        '3003': 'Broome',
        '14015': 'Darwin',
        '31011': 'Cairns',
        '40913': 'Brisbane',
        '66037': 'Sydney',
        '86282': 'Melbourne',
        '23034': 'Adelaide',
        '94029': 'Hobart'
    };

    var rainfall;

    return {
        getRainfall: function () {
            return rainfall = rainfall || $http.get('Data/TotalRain.csv').then(function (response) {
                return response.data;
            }).then(function (csvString) {
                var records = csvString.split('\n');
                var fields = records.shift().split(',');

                var name = 0;
                var year = 1;
                var month = 2;
                var day = 3;
                var rain = 4;

                records = records.map(function (row) { return row.split(','); });
                records = records.filter(function (row) { return !isNaN(parseInt(row[rain])); });
                records = records.filter(function (row) {
                    var date = new Date(row[year], row[month], row[day]);
                    var msecs = date.getTime();
                    return !isNaN(msecs);
                });

                var result = {};
                records.forEach(function (row) {
                    var date = new Date(row[year], row[month], row[day]);
                    var msecs = date.getTime();

                    result[msecs] = result[msecs] || {};
                    var datum = result[msecs];

                    datum.Rainfall = datum.Rainfall || {};

                    var locations = datum.Rainfall;
                    locations[nameMap[row[name]]] = row[rain];
                });

                return result;
            });
        },

    }
});<|endoftext|>


<commit_before>module.exports = function(api) {
  var validEnv = ['development', 'test', 'production']
  var currentEnv = api.env()
  var isDevelopmentEnv = api.env('development')
  var isProductionEnv = api.env('production')
  var isTestEnv = api.env('test')

  if (!validEnv.includes(currentEnv)) {
    throw new Error(
      'Please specify a valid `NODE_ENV` or ' +
        '`BABEL_ENV` environment variables. Valid values are "development", ' +
        '"test", and "production". Instead, received: ' +
        JSON.stringify(currentEnv) +
        '.'
    )
  }

  return {
    presets: [
      isTestEnv && [
        '@babel/preset-env',
        {
          targets: {
            node: 'current'
          }
        }
      ],
      (isProductionEnv || isDevelopmentEnv) && [
        '@babel/preset-env',
        {
          forceAllTransforms: true,
          useBuiltIns: 'entry',
          corejs: 3,
          modules: false,
          exclude: ['transform-typeof-symbol']
        }
      ]
    ].filter(Boolean),
    plugins: [
      'babel-plugin-macros',
      '@babel/plugin-syntax-dynamic-import',
      isTestEnv && 'babel-plugin-dynamic-import-node',
      '@babel/plugin-transform-destructuring',
      [
        '@babel/plugin-proposal-class-properties',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-proposal-object-rest-spread',
        {
          useBuiltIns: true
        }
      ],
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: false
        }
      ],
      [
        '@babel/plugin-transform-regenerator',
        {
          async: false
        }
      ]
    ].filter(Boolean)
  }
}
<commit_msg>Update babel config to remove deprecation warning<commit_after>module.exports = function(api) {
  var validEnv = ['development', 'test', 'production']
  var currentEnv = api.env()
  var isDevelopmentEnv = api.env('development')
  var isProductionEnv = api.env('production')
  var isTestEnv = api.env('test')

  if (!validEnv.includes(currentEnv)) {
    throw new Error(
      'Please specify a valid `NODE_ENV` or ' +
        '`BABEL_ENV` environment variables. Valid values are "development", ' +
        '"test", and "production". Instead, received: ' +
        JSON.stringify(currentEnv) +
        '.'
    )
  }

  return {
    presets: [
      isTestEnv && [
        '@babel/preset-env',
        {
          targets: {
            node: 'current'
          }
        }
      ],
      (isProductionEnv || isDevelopmentEnv) && [
        '@babel/preset-env',
        {
          forceAllTransforms: true,
          useBuiltIns: 'entry',
          corejs: 3,
          modules: false,
          exclude: ['transform-typeof-symbol']
        }
      ]
    ].filter(Boolean),
    plugins: [
      'babel-plugin-macros',
      '@babel/plugin-syntax-dynamic-import',
      isTestEnv && 'babel-plugin-dynamic-import-node',
      '@babel/plugin-transform-destructuring',
      [
        '@babel/plugin-proposal-class-properties',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-proposal-private-methods',
        {
          loose: true
        }
      ],
      [
        '@babel/plugin-proposal-object-rest-spread',
        {
          useBuiltIns: true
        }
      ],
      [
        '@babel/plugin-transform-runtime',
        {
          helpers: false
        }
      ],
      [
        '@babel/plugin-transform-regenerator',
        {
          async: false
        }
      ]
    ].filter(Boolean)
  }
}
<|endoftext|>


<commit_before>;(function () {

  // Initial Setup
  // -------------

  var root = this;
  var _ = root._;
  var Backbone = root.Backbone;

  // Initialize the Ext namespace
  Backbone.Ext || (Backbone.Ext = {});

  // Backbone.Ext.View
  // -----------------

  // Extends the default Backbone.View with placeholder methods for clearing
  // the element and doing any event handler cleanup. It also ensures that
  // all view elements will have a `data-cid` attribute for referencing views
  // so we can direct delegated events to the correct child view.
  var View = Backbone.Ext.View = Backbone.View.extend({
    // The default release method checks for a `model` or `collection` and
    // unbinds events associated with `this` context.
    release: function () {
      if (this.model) {
        this.model.off(null, null, this);
        this.model = null;
      }
      if (this.collection) {
        this.collection.off(null, null, this);
        this.collection = null;
      }
      return this;
    },

    // The default clear method just sets the html to an empty string.
    clear: function () {
      this.$el.html('');
      return this;
    },

    // Overriden `setElement` method which adds the `data-cid` attribute.
    setElement: function (element, delegate) {
      Backbone.View.prototype.setElement.call(this, element, delegate);

      // Add a custom attribute to the element for delegation tracking
      this.$el.attr('data-cid', this.cid);
      return this;
    },

    // Generates placeholder html for this particular view. This may be called
    // in a template for a CompositeView.
    placeholder: function () {
      return '<view data-cid="' + this.cid + '" />';
    }
  });

  // Static method to setup proxy methods to $el.
  View.enableElementProxy = function () {
    var methods = _.flatten(arguments);
    _.each(methods, function (method) {
      View.prototype[method] = function () {
        this.$el[method].apply(this.$el, _.toArray(arguments));
        return this;
      };
    });
  };

  // jQuery/Zepto methods to proxy. This will let us just call `view.show()`
  // and have it show the view's element.
  View.enableElementProxy('show', 'hide');

  // Backbone.Ext.CompositeView
  // --------------------------

  // A special view that lets you register child views that can then have
  // their events delegated. This lets you write your child views like they
  // are handling their own events, but have the parent CompositeView act as
  // a delegate so you don't have thousands of event handlers for views with
  // many children. It also provides a method for replacing placeholder
  // elements with the correct child views.

  // Cached regex to split event keys.
  var delegateEventSplitter = /^(\S+)\s*(.*)$/;

  var CompositeView = Backbone.Ext.CompositeView = View.extend({
    // Override the default constructor to initialize internal variables.
    constructor: function (options) {
      this._reset();
      Backbone.Ext.View.call(this, options);
    },

    // Release child views and clear caches
    release: function () {
      this._releaseChildren();
      this._reset();
      return View.prototype.release.call(this);
    },

    // Clear child views first
    clear: function () {
      this._clearChildren();
      return View.prototype.clear.call(this);
    },

    // Registers a view as a child. If a selector is provided,
    // `undelegateEvents` will be called on it since that means its events
    // need to be delegated via the parent instead. Use tha `at` option to have
    // the view spliced into `children` at the specified index.
    registerChild: function (view, options) {
      options || (options = {});
      var cid = view.cid;
      var selector = options.selector;
      var index = options.at != null ? options.at : this.children.length;
      if (selector) this._selectorByCid[cid] = selector, view.undelegateEvents();
      if (!this._childrenByCid[cid]) this.children.splice(index, 0, view);
      return this._childrenByCid[cid] = view;
    },

    // Removes a child from the internal cache. Passing `at` as an option will
    // deregister the child at the specified index within `children`. If an
    // `at` index is provided, the `view` argument will be ignored.
    deregisterChild: function (view, options) {
      options || (options = {});
      var index = options.at != null ? options.at : _.indexOf(this.children, view);
      var delView;
      if (~index) {
        delView = this.children[index];
        var cid = delView.cid;
        delete this._childrenByCid[cid];
        delete this._selectorByCid[cid];
        this.children.splice(index, 1);
      }
      return delView || false;
    },

    // Finds all placeholders and replaces them with the specified child views.
    placeChildren: function (options) {
      options || (options = {});
      var byCid = this._childrenByCid;
      var selector = this.placeholderSelector || Backbone.Ext.placeholderSelector;
      this.$(selector).each(function () {
        var $el = $(this), cid = $el.attr('data-cid');
        var view = byCid[cid];
        if (view) {
          if (options.render) view.render();
          $el.replaceWith(view.el);
        }
      });
      return this;
    },

    // Call `delegateChildEvents` also.
    delegateEvents: function (events) {
      Backbone.Ext.View.prototype.delegateEvents.call(this, events);
      this.delegateChildEvents();
    },

    // Loops over the registered children and sets up delegations on a first-
    // come-first-serve basis. Since view `events` can be a function, and thus
    // generated dynamically, this will use whatever the first view for a
    // selector provides and ignore the others.
    delegateChildEvents: function () {
      this.undelegateChildEvents();
      var delegations = {}, i, view;
      for (i = 0; (view = this.children[i]); i++) {
        var prefix = this._selectorByCid[view.cid];
        if (!prefix || delegations[prefix]) continue;
        delegations[prefix] = true;
        var events = getValue(view, 'events');
        for (var key in events) {
          var method = events[key];
          var match = key.match(delegateEventSplitter);
          var eventName = match[1] + '.delegateEvents' + this.cid + '.child';
          var selector = match[2] ? prefix + ' ' + match[2] : prefix;
          var proxy = _.bind(this._childEventProxy, this, prefix, method);
          this.$el.delegate(selector, eventName, proxy);
        }
      }
    },

    // Remove all delegations for child views.
    undelegateChildEvents: function () {
      this.$el.unbind('.delegateEvents' + this.cid + '.child');
    },

    // All delegations for child views run through this handler, which is
    // curried with the selector and method. It grabs the cid from the
    // target element and calls the method on the appropriate view.
    _childEventProxy: function (selector, method, e) {
      var cid = $(e.currentTarget).closest(selector).attr('data-cid')
      var view = this._childrenByCid[cid];
      if (!_.isFunction(method)) method = view[method];
      return method.call(view, e);
    },

    // Call `release` on all the children.
    _releaseChildren: function () {
      _.invoke(this.children, 'release');
    },

    // Call `clear` on all the children and detach it from the DOM so they
    // dont't lose their event handlers.
    _clearChildren: function () {
      var view, i;
      for (i = 0; (view = this.children[i]); i++) {
        view.clear();
        view.$el.detach();
      }
    },

    // Reset the internal cache of children.
    _reset: function () {
      this.children = [];
      this._childrenByCid = {};
      this._selectorByCid = {};
    }
  });

  // Alias `deregisterChild` to `unregisterChild`.
  CompositeView.prototype.unregisterChild = CompositeView.prototype.deregisterChild;

  // Selector used to find placeholder tags. If you override the default
  // `placeholder` method on Backbone.Ext.View to spit out a different tag,
  // you should change this. You can also configure it on individual
  // CompositeViews by setting an attribute of the same name.
  Backbone.Ext.placeholderSelector = 'view';

  // Backbone.Ext.MultiRouter
  // ------------------------

  // Backbone.History only allows one callback per route, but for more modular
  // applications, it can be helpful to have multiple routers that may share
  // the same routes but update different parts of the application. Write your
  // routers as you normally would, then use MultiRouter to glue them
  // together.

  var MultiRouter = Backbone.Ext.MultiRouter = Backbone.Router.extend({
    // Override the default constructor to initialize internal variables.
    constructor: function (options) {
      this._handlers = [];
      Backbone.Router.call(this, options);
    },

    // Override so route calls run through our internal handler instead of
    // binding directly to Backbone.history
    route: function () {
      var args = arguments;
      this._bindHandlers(this, gatherHandlers(function () {
        Backbone.Router.prototype.route.apply(this, args);
      }, this));
      return this;
    },

    // Given a router class and options, creates and returns a new instance
    // whose routes run through our internal handler.
    createRouter: function (routerClass, options) {
      var router;
      var handlers = gatherHandlers(function () {
        router = new routerClass(options);
      }, this);
      this._bindHandlers(router, handlers);
      return router;
    },

    // Given a route regex, registers a callback on Backbone.history that will
    // scan through all our registered routers and invoke methods that match
    // the route. This uses `_.any` just like Backbone.history would, so only
    // only one route will fire _within_ each router.
    _route: function (route) {
      Backbone.history.route(route, _.bind(function (fragment) {
        _.each(this._handlers, function (pair) {
          _.any(pair[1], function (handler) {
            if (handler.route.test(fragment)) {
              handler.callback(fragment);
              return true;
            }
          });
        });
      }, this));
    },

    // Given a router and a set of handlers in the form of {route, handler},
    // push the handlers onto the internal cache and register the routes.
    _bindHandlers: function (router, handlers) {
      var pair = _.find(this._handlers, function (pair) {
        return pair[0] === router;
      });
      if (pair) {
        pair[1].unshift(handlers);
      } else {
        pair = [router, handlers];
        this._handlers.push(pair);
      }
      _.each(handlers, function (handler) {
        this._route(handler.route);
      }, this);
    }
  });

  // Swaps out Backbone.history.route with its own method that will gather
  // the routes registered during the invocation of `func`.
  var gatherHandlers = function (func, context) {
    Backbone.history || (Backbone.history = new Backbone.History);
    var history = {handlers: []};
    var oldRoute = Backbone.history.route;
    Backbone.history.route = function () {
      oldRoute.apply(history, arguments);
    };
    func.call(context || window);
    Backbone.history.route = oldRoute;
    return history.handlers;
  };

  // If the value is a function, return the result of the function.
  var getValue = function(object, prop) {
    if (!(object && object[prop])) return null;
    return _.isFunction(object[prop]) ? object[prop]() : object[prop];
  };

}).call(this);
<commit_msg>Moved placeholder generation to the composite view instead of the child views as its something the parent should have control over.<commit_after>;(function () {

  // Initial Setup
  // -------------

  var root = this;
  var _ = root._;
  var Backbone = root.Backbone;

  // Initialize the Ext namespace
  Backbone.Ext || (Backbone.Ext = {});

  // Backbone.Ext.View
  // -----------------

  // Extends the default Backbone.View with placeholder methods for clearing
  // the element and doing any event handler cleanup. It also ensures that
  // all view elements will have a `data-cid` attribute for referencing views
  // so we can direct delegated events to the correct child view.
  var View = Backbone.Ext.View = Backbone.View.extend({
    // The default release method checks for a `model` or `collection` and
    // unbinds events associated with `this` context.
    release: function () {
      if (this.model) {
        this.model.off(null, null, this);
        this.model = null;
      }
      if (this.collection) {
        this.collection.off(null, null, this);
        this.collection = null;
      }
      return this;
    },

    // The default clear method just sets the html to an empty string.
    clear: function () {
      this.$el.html('');
      return this;
    },

    // Overriden `setElement` method which adds the `data-cid` attribute.
    setElement: function (element, delegate) {
      Backbone.View.prototype.setElement.call(this, element, delegate);

      // Add a custom attribute to the element for delegation tracking
      this.$el.attr('data-cid', this.cid);
      return this;
    }
  });

  // Static method to setup proxy methods to $el.
  View.enableElementProxy = function () {
    var methods = _.flatten(arguments);
    _.each(methods, function (method) {
      View.prototype[method] = function () {
        this.$el[method].apply(this.$el, _.toArray(arguments));
        return this;
      };
    });
  };

  // jQuery/Zepto methods to proxy. This will let us just call `view.show()`
  // and have it show the view's element.
  View.enableElementProxy('show', 'hide');

  // Backbone.Ext.CompositeView
  // --------------------------

  // A special view that lets you register child views that can then have
  // their events delegated. This lets you write your child views like they
  // are handling their own events, but have the parent CompositeView act as
  // a delegate so you don't have thousands of event handlers for views with
  // many children. It also provides a method for replacing placeholder
  // elements with the correct child views.

  // Cached regex to split event keys.
  var delegateEventSplitter = /^(\S+)\s*(.*)$/;

  var CompositeView = Backbone.Ext.CompositeView = View.extend({
    // Override the default constructor to initialize internal variables.
    constructor: function (options) {
      this._reset();
      Backbone.Ext.View.call(this, options);
    },

    // Release child views and clear caches
    release: function () {
      this._releaseChildren();
      this._reset();
      return View.prototype.release.call(this);
    },

    // Clear child views first
    clear: function () {
      this._clearChildren();
      return View.prototype.clear.call(this);
    },

    // Generates placeholder html for the supplied view. This should be called
    // in templates.
    makePlaceholder: function (view) {
      return '<view data-cid="' + view.cid + '" />';
    }

    // Registers a view as a child. If a selector is provided,
    // `undelegateEvents` will be called on it since that means its events
    // need to be delegated via the parent instead. Use tha `at` option to have
    // the view spliced into `children` at the specified index.
    registerChild: function (view, options) {
      options || (options = {});
      var cid = view.cid;
      var selector = options.selector;
      var index = options.at != null ? options.at : this.children.length;
      if (selector) this._selectorByCid[cid] = selector, view.undelegateEvents();
      if (!this._childrenByCid[cid]) this.children.splice(index, 0, view);
      return this._childrenByCid[cid] = view;
    },

    // Removes a child from the internal cache. Passing `at` as an option will
    // deregister the child at the specified index within `children`. If an
    // `at` index is provided, the `view` argument will be ignored.
    deregisterChild: function (view, options) {
      options || (options = {});
      var index = options.at != null ? options.at : _.indexOf(this.children, view);
      var delView;
      if (~index) {
        delView = this.children[index];
        var cid = delView.cid;
        delete this._childrenByCid[cid];
        delete this._selectorByCid[cid];
        this.children.splice(index, 1);
      }
      return delView || false;
    },

    // Finds all placeholders and replaces them with the specified child views.
    placeChildren: function (options) {
      options || (options = {});
      var byCid = this._childrenByCid;
      var selector = this.placeholderSelector || Backbone.Ext.placeholderSelector;
      this.$(selector).each(function () {
        var $el = $(this), cid = $el.attr('data-cid');
        var view = byCid[cid];
        if (view) {
          if (options.render) view.render();
          $el.replaceWith(view.el);
        }
      });
      return this;
    },

    // Call `delegateChildEvents` also.
    delegateEvents: function (events) {
      Backbone.Ext.View.prototype.delegateEvents.call(this, events);
      this.delegateChildEvents();
    },

    // Loops over the registered children and sets up delegations on a first-
    // come-first-serve basis. Since view `events` can be a function, and thus
    // generated dynamically, this will use whatever the first view for a
    // selector provides and ignore the others.
    delegateChildEvents: function () {
      this.undelegateChildEvents();
      var delegations = {}, i, view;
      for (i = 0; (view = this.children[i]); i++) {
        var prefix = this._selectorByCid[view.cid];
        if (!prefix || delegations[prefix]) continue;
        delegations[prefix] = true;
        var events = getValue(view, 'events');
        for (var key in events) {
          var method = events[key];
          var match = key.match(delegateEventSplitter);
          var eventName = match[1] + '.delegateEvents' + this.cid + '.child';
          var selector = match[2] ? prefix + ' ' + match[2] : prefix;
          var proxy = _.bind(this._childEventProxy, this, prefix, method);
          this.$el.delegate(selector, eventName, proxy);
        }
      }
    },

    // Remove all delegations for child views.
    undelegateChildEvents: function () {
      this.$el.unbind('.delegateEvents' + this.cid + '.child');
    },

    // All delegations for child views run through this handler, which is
    // curried with the selector and method. It grabs the cid from the
    // target element and calls the method on the appropriate view.
    _childEventProxy: function (selector, method, e) {
      var cid = $(e.currentTarget).closest(selector).attr('data-cid')
      var view = this._childrenByCid[cid];
      if (!_.isFunction(method)) method = view[method];
      return method.call(view, e);
    },

    // Call `release` on all the children.
    _releaseChildren: function () {
      _.invoke(this.children, 'release');
    },

    // Call `clear` on all the children and detach it from the DOM so they
    // dont't lose their event handlers.
    _clearChildren: function () {
      var view, i;
      for (i = 0; (view = this.children[i]); i++) {
        view.clear();
        view.$el.detach();
      }
    },

    // Reset the internal cache of children.
    _reset: function () {
      this.children = [];
      this._childrenByCid = {};
      this._selectorByCid = {};
    }
  });

  // Alias `deregisterChild` to `unregisterChild`.
  CompositeView.prototype.unregisterChild = CompositeView.prototype.deregisterChild;

  // Selector used to find placeholder tags. If you override the default
  // `placeholder` method on Backbone.Ext.View to spit out a different tag,
  // you should change this. You can also configure it on individual
  // CompositeViews by setting an attribute of the same name.
  Backbone.Ext.placeholderSelector = 'view';

  // Backbone.Ext.MultiRouter
  // ------------------------

  // Backbone.History only allows one callback per route, but for more modular
  // applications, it can be helpful to have multiple routers that may share
  // the same routes but update different parts of the application. Write your
  // routers as you normally would, then use MultiRouter to glue them
  // together.

  var MultiRouter = Backbone.Ext.MultiRouter = Backbone.Router.extend({
    // Override the default constructor to initialize internal variables.
    constructor: function (options) {
      this._handlers = [];
      Backbone.Router.call(this, options);
    },

    // Override so route calls run through our internal handler instead of
    // binding directly to Backbone.history
    route: function () {
      var args = arguments;
      this._bindHandlers(this, gatherHandlers(function () {
        Backbone.Router.prototype.route.apply(this, args);
      }, this));
      return this;
    },

    // Given a router class and options, creates and returns a new instance
    // whose routes run through our internal handler.
    createRouter: function (routerClass, options) {
      var router;
      var handlers = gatherHandlers(function () {
        router = new routerClass(options);
      }, this);
      this._bindHandlers(router, handlers);
      return router;
    },

    // Given a route regex, registers a callback on Backbone.history that will
    // scan through all our registered routers and invoke methods that match
    // the route. This uses `_.any` just like Backbone.history would, so only
    // only one route will fire _within_ each router.
    _route: function (route) {
      Backbone.history.route(route, _.bind(function (fragment) {
        _.each(this._handlers, function (pair) {
          _.any(pair[1], function (handler) {
            if (handler.route.test(fragment)) {
              handler.callback(fragment);
              return true;
            }
          });
        });
      }, this));
    },

    // Given a router and a set of handlers in the form of {route, handler},
    // push the handlers onto the internal cache and register the routes.
    _bindHandlers: function (router, handlers) {
      var pair = _.find(this._handlers, function (pair) {
        return pair[0] === router;
      });
      if (pair) {
        pair[1].unshift(handlers);
      } else {
        pair = [router, handlers];
        this._handlers.push(pair);
      }
      _.each(handlers, function (handler) {
        this._route(handler.route);
      }, this);
    }
  });

  // Swaps out Backbone.history.route with its own method that will gather
  // the routes registered during the invocation of `func`.
  var gatherHandlers = function (func, context) {
    Backbone.history || (Backbone.history = new Backbone.History);
    var history = {handlers: []};
    var oldRoute = Backbone.history.route;
    Backbone.history.route = function () {
      oldRoute.apply(history, arguments);
    };
    func.call(context || window);
    Backbone.history.route = oldRoute;
    return history.handlers;
  };

  // If the value is a function, return the result of the function.
  var getValue = function(object, prop) {
    if (!(object && object[prop])) return null;
    return _.isFunction(object[prop]) ? object[prop]() : object[prop];
  };

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


<commit_before>import React from 'react'
import { Button, Comment, Form, Icon } from 'semantic-ui-react'
import UserPopup from './UserPopup'

const Timeline = ({ tweets }) => (
  <Comment.Group>
    {tweets.map(t => {
      let tweet = t
      let retweet = t.retweeted_status != null
      if (retweet) {
        tweet = t.retweeted_status
      }
      return (
        <Comment key={t.id}>
          {retweet ? (
            <Comment.Content>
              <Icon name="retweet" color="green" />
              <UserPopup user={t.user}>
                <Comment.Author as='a'>{t.user.name}</Comment.Author>
              </UserPopup>
              <Comment.Metadata>

@{t.user.screen_name}

{t.created_at}

              </Comment.Metadata>
            </Comment.Content>
          ) : null}
          <UserPopup user={tweet.user}>
            <Comment.Avatar src={tweet.user.profile_image_url_https} />
          </UserPopup>
          <Comment.Content>
            <UserPopup user={tweet.user}>
              <Comment.Author as='a'>{tweet.user.name}</Comment.Author>
            </UserPopup>
            <Comment.Metadata>

@{tweet.user.screen_name}

{tweet.created_at}

            </Comment.Metadata>
            <Comment.Text>{tweet.text}</Comment.Text>
          </Comment.Content>
        </Comment>
      )
    })}
  </Comment.Group>
)

export default Timeline
<commit_msg>Add links to like tweets<commit_after>import React from 'react'
import { Button, Comment, Form, Icon } from 'semantic-ui-react'
import UserPopup from './UserPopup'

const Timeline = ({ tweets }) => (
  <Comment.Group>
    {tweets.map(t => {
      let tweet = t
      let retweet = t.retweeted_status != null
      if (retweet) {
        tweet = t.retweeted_status
      }
      return (
        <Comment key={t.id}>
          {retweet ? (
            <Comment.Content>
              <Icon name="retweet" color="green" />
              <UserPopup user={t.user}>
                <Comment.Author as='a'>{t.user.name}</Comment.Author>
              </UserPopup>
              <Comment.Metadata>

@{t.user.screen_name}

{t.created_at}

              </Comment.Metadata>
            </Comment.Content>
          ) : null}
          <UserPopup user={tweet.user}>
            <Comment.Avatar src={tweet.user.profile_image_url_https} />
          </UserPopup>
          <Comment.Content>
            <UserPopup user={tweet.user}>
              <Comment.Author as='a'>{tweet.user.name}</Comment.Author>
            </UserPopup>
            <Comment.Metadata>

@{tweet.user.screen_name}

<a href={`https://twitter.com/intent/like?tweet_id=${tweet.id_str}`} target="_blank">{tweet.created_at}

            </Comment.Metadata>
            <Comment.Text>{tweet.text}</Comment.Text>
          </Comment.Content>
        </Comment>
      )
    })}
  </Comment.Group>
)

export default Timeline
<|endoftext|>


<commit_before>/**
 * @class  Backbone.Rel
 * @name   Backbone Relationships
 * @desc   Backbone.Rel extends your Backbone models with a lightweight relationships manager
*/
(function () {

  var singularize
    , root = this
    , _ = root._;

  if (!_ && (typeof require !== 'undefined')) {
    _ = require('underscore')._;
  }

  // poor singularize fallback
  singularize = _.memoize(_.singularize || function (word) {
    return word.replace(/s$/, '');
  });

  /**
   * Get the relationship options
   *
   * @param {Model} self
   * @param {String} rel
   * @param {String} key
   * @return {Collection|Null}
   */
  function getOptions(self, rel, key) {
    return self[rel]
      ? self[rel]()[key]
      : null;
  }

  /**
   * Constructor
   *
   * @param {Model|Collection} self
   * @param {String} key
   * @param {String} type [Collection|Model]
   * @return {RelHandler}
   */
  function RelHandler(self, key, type) {
    this.self = self;
    this.key = key;
    this.type = type;
  }

  /**
   * Searches the relations for the self object
   *
   * @return {Model|Array<Model>|Null}
   */
  RelHandler.prototype.searchRelations = function () {
    if (this.type === 'Collection') {
      return this.handleBelongsTo();
    } else {
      return this.handleHasMany() || this.handleBelongsTo();
    }
  };

  /**
   * Gets the belongsTo id attribute
   *
   * @return {Number}
   */
  RelHandler.prototype.findBelongsToIdAttribute = function () {
    var id_attr = singularize(this.key) + '_id';

    if (this.type === 'Model') {
      return this.self.get(id_attr);
    } else {
      return this.self[id_attr];
    }
  };

  /**
   * Handles the belongsTo relationship
   *
   * @return {Model|Null}
   */
  RelHandler.prototype.handleBelongsTo = function () {
    var collection = getOptions(this.self, 'belongsTo', this.key)
      , eventBus = this.self.relEventBus
      , result
      , id;

    if (!collection) {
      return null;
    }

    if (_.isFunction(collection)) {
      result = collection(this.self);
    } else {
      id = this.findBelongsToIdAttribute();
      result = collection.get(id);

      if (eventBus && _.isNumber(id) && !result) {
        eventBus.trigger && eventBus.trigger('backbone-rel:missing', this.key, id);
      }
    }

    return result || null;
  };

  /**
   * Handles the hasMany relationship
   *
   * @return {Array<Model>|Null}
   */
  RelHandler.prototype.handleHasMany = function () {
    var options = getOptions(this.self, 'hasMany', this.key);

    function filter(el) {
      return el.get(options.id) === this.id;
    }

    if (!options) {
      return null;
    }

    if (_.isFunction(options)) {
      return options(this.self);
    }

    if (!options.collection) {
      throw Error('No collection was given');
    }

    return options.collection.filter(_.bind(options.filter || filter, this.self));
  };

  /**
   * Gets an attribute over a relation
   *
   * @param {String} relation
   * @param {String} attribute
   * @param {<A>} default_value
   * @return {<A>}
   */
  function relResult(relation, attribute, default_value) {
    var rel = this.rel(relation);

    if (rel) {
      if (_.isArray(rel)) {
        return _.map(rel, function (r) {
          return _.result(r, attribute);
        });
      } else {
        return _.result(rel, attribute);
      }
    } else {
      return default_value || null;
    }
  }

  /**
   * Gets an attribute over a relation
   *
   * @param {String} relation
   * @param {String} attribute
   * @param {<A>} default_value
   * @return {<A>}
   */
  function relGet(relation, attribute, default_value) {
    var rel = this.rel(relation);

    if (rel) {
      if (_.isArray(rel)) {
        return _.map(rel, function (r) {
          return r.get(attribute);
        });
      } else {
        return rel.get(attribute);
      }
    } else {
      return default_value || null;
    }
  }

  /**
   * Computes and gets the relationship
   *
   * @param {String} type [Collection|Model]
   * @return {Model|Array|Null}
   */
  function rel(type) {
    return function (key) {
      var self = this
        , keys = key.split('.');

      // kind of monadic accesor
      if (keys.length > 1) {
        return _.reduce(keys, function (memo, key) {
          if (_.isUndefined(memo)) {
            return self.rel(key);
          } else if (_.isArray(memo)) {
            return _.flatten(_.map(memo, function (item) {
              return item.rel(key);
            }));
          } else if (memo) {
            return memo.rel(key);
          } else {
            return null;
          }
        }, undefined);
      }

      return (new RelHandler(self, key, type)).searchRelations();
    };
  }

  _.extend(Backbone.Model.prototype, {rel: rel('Model'), relGet: relGet, relResult: relResult, relEventBus: null});
  _.extend(Backbone.Collection.prototype, {rel: rel('Collection'), relGet: relGet, relResult: relResult, relEventBus: null});

}());
<commit_msg>Use Backbone.Collection where method instead of filter<commit_after>/**
 * @class  Backbone.Rel
 * @name   Backbone Relationships
 * @desc   Backbone.Rel extends your Backbone models with a lightweight relationships manager
*/
(function () {

  var singularize
    , root = this
    , _ = root._;

  if (!_ && (typeof require !== 'undefined')) {
    _ = require('underscore')._;
  }

  // poor singularize fallback
  singularize = _.memoize(_.singularize || function (word) {
    return word.replace(/s$/, '');
  });

  /**
   * Get the relationship options
   *
   * @param {Model} self
   * @param {String} rel
   * @param {String} key
   * @return {Collection|Null}
   */
  function getOptions(self, rel, key) {
    return self[rel]
      ? self[rel]()[key]
      : null;
  }

  /**
   * Constructor
   *
   * @param {Model|Collection} self
   * @param {String} key
   * @param {String} type [Collection|Model]
   * @return {RelHandler}
   */
  function RelHandler(self, key, type) {
    this.self = self;
    this.key = key;
    this.type = type;
  }

  /**
   * Searches the relations for the self object
   *
   * @return {Model|Array<Model>|Null}
   */
  RelHandler.prototype.searchRelations = function () {
    if (this.type === 'Collection') {
      return this.handleBelongsTo();
    } else {
      return this.handleHasMany() || this.handleBelongsTo();
    }
  };

  /**
   * Gets the belongsTo id attribute
   *
   * @return {Number}
   */
  RelHandler.prototype.findBelongsToIdAttribute = function () {
    var id_attr = singularize(this.key) + '_id';

    if (this.type === 'Model') {
      return this.self.get(id_attr);
    } else {
      return this.self[id_attr];
    }
  };

  /**
   * Handles the belongsTo relationship
   *
   * @return {Model|Null}
   */
  RelHandler.prototype.handleBelongsTo = function () {
    var collection = getOptions(this.self, 'belongsTo', this.key)
      , eventBus = this.self.relEventBus
      , result
      , id;

    if (!collection) {
      return null;
    }

    if (_.isFunction(collection)) {
      result = collection(this.self);
    } else {
      id = this.findBelongsToIdAttribute();
      result = collection.get(id);

      if (eventBus && _.isNumber(id) && !result) {
        eventBus.trigger && eventBus.trigger('backbone-rel:missing', this.key, id);
      }
    }

    return result || null;
  };

  /**
   * Handles the hasMany relationship
   *
   * @return {Array<Model>|Null}
   */
  RelHandler.prototype.handleHasMany = function () {
    var options = getOptions(this.self, 'hasMany', this.key)
      , where = {};

    if (!options) {
      return null;
    }

    if (_.isFunction(options)) {
      return options(this.self);
    }

    if (!options.collection) {
      throw Error('No collection was given');
    }

    if (options.filter) {
      return options.collection.filter(_.bind(options.filter, this.self));
    }

    if (options.id) {
      where[options.id] = this.self.id;
      return options.collection.where(where);
    }

    throw Error('Unknown options');
  };

  /**
   * Gets an attribute over a relation
   *
   * @param {String} relation
   * @param {String} attribute
   * @param {<A>} default_value
   * @return {<A>}
   */
  function relResult(relation, attribute, default_value) {
    var rel = this.rel(relation);

    if (rel) {
      if (_.isArray(rel)) {
        return _.map(rel, function (r) {
          return _.result(r, attribute);
        });
      } else {
        return _.result(rel, attribute);
      }
    } else {
      return default_value || null;
    }
  }

  /**
   * Gets an attribute over a relation
   *
   * @param {String} relation
   * @param {String} attribute
   * @param {<A>} default_value
   * @return {<A>}
   */
  function relGet(relation, attribute, default_value) {
    var rel = this.rel(relation);

    if (rel) {
      if (_.isArray(rel)) {
        return _.map(rel, function (r) {
          return r.get(attribute);
        });
      } else {
        return rel.get(attribute);
      }
    } else {
      return default_value || null;
    }
  }

  /**
   * Computes and gets the relationship
   *
   * @param {String} type [Collection|Model]
   * @return {Model|Array|Null}
   */
  function rel(type) {
    return function (key) {
      var self = this
        , keys = key.split('.');

      // kind of monadic accesor
      if (keys.length > 1) {
        return _.reduce(keys, function (memo, key) {
          if (_.isUndefined(memo)) {
            return self.rel(key);
          } else if (_.isArray(memo)) {
            return _.flatten(_.map(memo, function (item) {
              return item.rel(key);
            }));
          } else if (memo) {
            return memo.rel(key);
          } else {
            return null;
          }
        }, undefined);
      }

      return (new RelHandler(self, key, type)).searchRelations();
    };
  }

  _.extend(Backbone.Model.prototype, {rel: rel('Model'), relGet: relGet, relResult: relResult, relEventBus: null});
  _.extend(Backbone.Collection.prototype, {rel: rel('Collection'), relGet: relGet, relResult: relResult, relEventBus: null});

}());
<|endoftext|>