<commit_before>/**
 * The Settings Modul reads the settings out of settings.json and provides
 * this information to the other modules
 */

/*
 * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require("fs");
var os = require("os");
var path = require('path');
var argv = require('./Cli').argv;
var npm = require("npm/lib/npm.js");
var vm = require('vm');
var log4js = require("log4js");
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;

/* Root path of the installation */
exports.root = path.normalize(path.join(npm.dir, ".."));

/**
 * The app title, visible e.g. in the browser window
 */
exports.title = "Etherpad Lite";

/**
 * The app favicon fully specified url, visible e.g. in the browser window
 */
exports.favicon = "favicon.ico";
exports.faviconPad = "../" + exports.favicon;
exports.faviconTimeslider = "../../" + exports.favicon;

/**
 * The IP ep-lite should listen to
 */
exports.ip = "0.0.0.0";

/**
 * The Port ep-lite should listen to
 */
exports.port = process.env.PORT || 9001;

/**
 * The SSL signed server key and the Certificate Authority's own certificate
 * default case: ep-lite does *not* use SSL. A signed server key is not required in this case.
 */
exports.ssl = false;

/**
 * socket.io transport methods
 **/
exports.socketTransportProtocols = ['xhr-polling', 'jsonp-polling', 'htmlfile'];

/*
 * The Type of the database
 */
exports.dbType = "dirty";
/**
 * This setting is passed with dbType to ueberDB to set up the database
 */
exports.dbSettings = { "filename" : path.join(exports.root, "dirty.db") };

/**
 * The default Text of a new pad
 */
exports.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n";

/**
 * A flag that requires any user to have a valid session (via the api) before accessing a pad
 */
exports.requireSession = false;

/**
 * A flag that prevents users from creating new pads
 */
exports.editOnly = false;

/**
 * Max age that responses will have (affects caching layer).
 */
exports.maxAge = 1000*60*60*6; // 6 hours

/**
 * A flag that shows if minification is enabled or not
 */
exports.minify = true;

/**
 * The path of the abiword executable
 */
exports.abiword = null;

/**
 * The log level of log4js
 */
exports.loglevel = "INFO";

/*
* log4js appender configuration
*/
exports.logconfig = { appenders: [{ type: "console" }]};

/*
* Session Key, do not sure this.
*/
exports.sessionKey = false;

/* This setting is used if you need authentication and/or
 * authorization. Note: /admin always requires authentication, and
 * either authorization by a module, or a user with is_admin set */
exports.requireAuthentication = false;
exports.requireAuthorization = false;
exports.users = {};

//checks if abiword is avaiable
exports.abiwordAvailable = function()
{
  if(exports.abiword != null)
  {
    return os.type().indexOf("Windows") != -1 ? "withoutPDF" : "yes";
  }
  else
  {
    return "no";
  }
}

exports.reloadSettings = function reloadSettings() {
  // Discover where the settings file lives
  var settingsFilename = argv.settings || "settings.json";
  settingsFilename = path.resolve(path.join(root, settingsFilename));

  var settingsStr;
  try{
    //read the settings sync
    settingsStr = fs.readFileSync(settingsFilename).toString();
  } catch(e){
    console.warn('No settings file found. Continuing using defaults!');
  }

  // try to parse the settings
  var settings;
  try {
    if(settingsStr) {
      settings = vm.runInContext('exports = '+settingsStr, vm.createContext(), "settings.json");
      settings = JSON.parse(JSON.stringify(settings)) // fix objects having constructors of other vm.context
    }
  }catch(e){
    console.error('There was an error processing your settings.json file: '+e.message);
    process.exit(1);
  }

  //loop trough the settings
  for(var i in settings)
  {
    //test if the setting start with a low character
    if(i.charAt(0).search("[a-z]") !== 0)
    {
      console.warn("Settings should start with a low character: '" + i + "'");
    }

    //we know this setting, so we overwrite it
    //or it's a settings hash, specific to a plugin
    if(exports[i] !== undefined || i.indexOf('ep_')==0)
    {
      exports[i] = settings[i];
    }
    //this setting is unkown, output a warning and throw it away
    else
    {
      console.warn("Unknown Setting: '" + i + "'. This setting doesn't exist or it was removed");
    }
  }

  log4js.configure(exports.logconfig);//Configure the logging appenders
  log4js.setGlobalLogLevel(exports.loglevel);//set loglevel
  log4js.replaceConsole();

  if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here
    exports.sessionKey = randomString(32);
    console.warn("You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts");
  }

  if(exports.dbType === "dirty"){
    console.warn("DirtyDB is used. This is fine for testing but not recommended for production.")
  }
}

// initially load settings
exports.reloadSettings();
<commit_msg>Fix path.join in Settings.js<commit_after>/**
 * The Settings Modul reads the settings out of settings.json and provides
 * this information to the other modules
 */

/*
 * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require("fs");
var os = require("os");
var path = require('path');
var argv = require('./Cli').argv;
var npm = require("npm/lib/npm.js");
var vm = require('vm');
var log4js = require("log4js");
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;

/* Root path of the installation */
exports.root = path.normalize(path.join(npm.dir, ".."));

/**
 * The app title, visible e.g. in the browser window
 */
exports.title = "Etherpad Lite";

/**
 * The app favicon fully specified url, visible e.g. in the browser window
 */
exports.favicon = "favicon.ico";
exports.faviconPad = "../" + exports.favicon;
exports.faviconTimeslider = "../../" + exports.favicon;

/**
 * The IP ep-lite should listen to
 */
exports.ip = "0.0.0.0";

/**
 * The Port ep-lite should listen to
 */
exports.port = process.env.PORT || 9001;

/**
 * The SSL signed server key and the Certificate Authority's own certificate
 * default case: ep-lite does *not* use SSL. A signed server key is not required in this case.
 */
exports.ssl = false;

/**
 * socket.io transport methods
 **/
exports.socketTransportProtocols = ['xhr-polling', 'jsonp-polling', 'htmlfile'];

/*
 * The Type of the database
 */
exports.dbType = "dirty";
/**
 * This setting is passed with dbType to ueberDB to set up the database
 */
exports.dbSettings = { "filename" : path.join(exports.root, "dirty.db") };

/**
 * The default Text of a new pad
 */
exports.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n";

/**
 * A flag that requires any user to have a valid session (via the api) before accessing a pad
 */
exports.requireSession = false;

/**
 * A flag that prevents users from creating new pads
 */
exports.editOnly = false;

/**
 * Max age that responses will have (affects caching layer).
 */
exports.maxAge = 1000*60*60*6; // 6 hours

/**
 * A flag that shows if minification is enabled or not
 */
exports.minify = true;

/**
 * The path of the abiword executable
 */
exports.abiword = null;

/**
 * The log level of log4js
 */
exports.loglevel = "INFO";

/*
* log4js appender configuration
*/
exports.logconfig = { appenders: [{ type: "console" }]};

/*
* Session Key, do not sure this.
*/
exports.sessionKey = false;

/* This setting is used if you need authentication and/or
 * authorization. Note: /admin always requires authentication, and
 * either authorization by a module, or a user with is_admin set */
exports.requireAuthentication = false;
exports.requireAuthorization = false;
exports.users = {};

//checks if abiword is avaiable
exports.abiwordAvailable = function()
{
  if(exports.abiword != null)
  {
    return os.type().indexOf("Windows") != -1 ? "withoutPDF" : "yes";
  }
  else
  {
    return "no";
  }
}

exports.reloadSettings = function reloadSettings() {
  // Discover where the settings file lives
  var settingsFilename = argv.settings || "settings.json";
  settingsFilename = path.resolve(path.join(exports.root, settingsFilename));

  var settingsStr;
  try{
    //read the settings sync
    settingsStr = fs.readFileSync(settingsFilename).toString();
  } catch(e){
    console.warn('No settings file found. Continuing using defaults!');
  }

  // try to parse the settings
  var settings;
  try {
    if(settingsStr) {
      settings = vm.runInContext('exports = '+settingsStr, vm.createContext(), "settings.json");
      settings = JSON.parse(JSON.stringify(settings)) // fix objects having constructors of other vm.context
    }
  }catch(e){
    console.error('There was an error processing your settings.json file: '+e.message);
    process.exit(1);
  }

  //loop trough the settings
  for(var i in settings)
  {
    //test if the setting start with a low character
    if(i.charAt(0).search("[a-z]") !== 0)
    {
      console.warn("Settings should start with a low character: '" + i + "'");
    }

    //we know this setting, so we overwrite it
    //or it's a settings hash, specific to a plugin
    if(exports[i] !== undefined || i.indexOf('ep_')==0)
    {
      exports[i] = settings[i];
    }
    //this setting is unkown, output a warning and throw it away
    else
    {
      console.warn("Unknown Setting: '" + i + "'. This setting doesn't exist or it was removed");
    }
  }

  log4js.configure(exports.logconfig);//Configure the logging appenders
  log4js.setGlobalLogLevel(exports.loglevel);//set loglevel
  log4js.replaceConsole();

  if(!exports.sessionKey){ // If the secretKey isn't set we also create yet another unique value here
    exports.sessionKey = randomString(32);
    console.warn("You need to set a sessionKey value in settings.json, this will allow your users to reconnect to your Etherpad Instance if your instance restarts");
  }

  if(exports.dbType === "dirty"){
    console.warn("DirtyDB is used. This is fine for testing but not recommended for production.")
  }
}

// initially load settings
exports.reloadSettings();
<|endoftext|>


<commit_before>function generateId(length) {
    var i, l = length || 10,
        an = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSXYZ',
        id = '';

    for(i=l;i--;) {
        id += an.charAt((Math.random()*an.length)+1);
    }
    return id;

}

function center(el) {
    var wH = $(window).height(),
        wW = $(window).width(),
        elH = el.height(),
        elW = el.width(),
        top = (wH - elH)/2,
        left = (wW - elW)/2;

        return {
            top: top
          , left: left
        };
}

function template(templateCache, contentTemplateUrl) {
    var temp = generateId(20);
    templateCache.put(temp, contentTemplateUrl);
    return temp;
}

angular.module('angular-shimmy', [])

.directive('shimmyButton', function($compile) {
    return {
        restrict: 'E'
      , scope: {
            shimmyClass:        '@'
          , shimmyContentClass: '@'
          , contentTemplate:    '@'
          , contentTemplateUrl: '@'
        }
      , tranclude: true
      , link: function(scope, elem, attrs) {
            var contentTemplate = attrs.contentTemplate || ""
              , contentTemplateUrl = attrs.contentTemplateUrl || ""
              , shimmyClass = attrs.shimmyClass || ""
              , shimmyContentClass = attrs.shimmyContentClass || ""
              , el = '<shimmy class="'+shimmyClass+'" shimmy-content-class="'+shimmyContentClass+'" content-template="'+contentTemplate+'" content-template-url="'+contentTemplateUrl+'"></shimmy>';

            elem.on('click', function() {
                document.body.appendChild($compile(el)(scope)[0]);
            });
        }
    };
})

.directive('shimmy', function($compile) {
    return {
        restrict: 'E'
      , scope: {
            contentTemplate:    '@'
          , contentTemplateUrl: '@'
          , shimmyContentClass:   '@'
        }
      , controller: function($scope, $q) {
            $scope.defer = $q.defer();
            return $scope.defer.promise;
        }
      , transclude: true
      , template: '<shimmy-content shimmy-content-class="{{shimmyContentClass}}" content-template="{{contentTemplate}}" content-template-url="{{contentTemplateUrl}}"><shimmy-close></shimmy-close></shimmy-content>'
      , link: function(scope, elem, attrs, ctrl) {
            scope.defer.resolve(elem);

            elem.on('click', function() {
                document.body.removeChild(elem[0]);
            });
        }
    };
})

.directive('shimmyContent', function($compile, $timeout, $templateCache) {
    return {
        restrict: 'E'
      , scope: {
            contentTemplate:    '@'
          , contentTemplateUrl: '@'
          , shimmyContentClass:   '@'
        }
      , tranclude: true
      , link: function(scope, elem, attrs) {
            var template = attrs.contentTemplate || $templateCache.get(attrs.contentTemplateUrl) || $templateCache.get(template($templateCache, attrs.contentTemplateUrl)) || '';

            elem.append($compile(template)(scope));
            elem.toggleClass(attrs.shimmyContentClass);
            $timeout(function() {
                var css = center(elem);
                elem.css(css);
                elem.on('click', function(e) {
                    e.stopPropagation();
                });
            });
        }
    };
})

.directive('shimmyClose', function() {
    return {
        restrict: 'E'
      , scope: {}
      , translude: true
      , require: '^shimmy'
      , template: '×'
      , link: function(scope, elem, attrs, ctrl) {
            ctrl.then(function(shimmy) {
                elem.on('click', function() {
                    document.body.removeChild(shimmy[0]);
                });
            });
        }
    };
});

// Eventually become an example

/*
    <shimmy-button shimmy-class="foo" shimmy-content-class="shim-test-class" content-template-url="foo.html">Shimmy on over</shimmy-button>

*/<commit_msg>Fixed closeable and shimmy-content-class attribute and an issue where the close button is compiled before it has access to it's parent controller.<commit_after>function generateId(length) {
    var i, l = length || 10,
        an = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSXYZ',
        id = '';

    for(i=l;i--;) {
        id += an.charAt((Math.random()*an.length)+1);
    }
    return id;

}

function center(el) {
    var wH = $(window).height(),
        wW = $(window).width(),
        elH = el.height(),
        elW = el.width(),
        top = (wH - elH)/2,
        left = (wW - elW)/2;

        return {
            top: top
          , left: left
        };
}

function template(templateCache, contentTemplateUrl) {
    var temp = generateId(20);
    templateCache.put(temp, contentTemplateUrl);
    return temp;
}

angular.module('angular-shimmy', [])

.directive('shimmyButton', function($compile) {
    return {
        restrict: 'E'
      , scope: {
            shimmyClass:        '@'
          , shimmyContentClass: '@'
          , contentTemplate:    '@'
          , contentTemplateUrl: '@'
          , closeable:          '@'
        }
      , tranclude: true
      , link: function(scope, elem, attrs) {
            var contentTemplate = attrs.contentTemplate || ""
              , contentTemplateUrl = attrs.contentTemplateUrl || ""
              , shimmyClass = attrs.shimmyClass || ""
              , shimmyContentClass = attrs.shimmyContentClass || ""
              , closeable = attrs.closeable || false
              , el = '<shimmy class="'+shimmyClass+'" shimmy-content-class="'+shimmyContentClass+'" closeable='+closeable+' content-template="'+contentTemplate+'" content-template-url="'+contentTemplateUrl+'"></shimmy>';

            elem.on('click', function() {
                document.body.appendChild($compile(el)(scope)[0]);
            });
        }
    };
})

.directive('shimmy', function($compile) {
    return {
        restrict: 'E'
      , scope: {
            contentTemplate:    '@'
          , contentTemplateUrl: '@'
          , shimmyContentClass: '@'
          , closeable:          '@'
        }
      , controller: function($scope, $q) {
            $scope.defer = $q.defer();
            return $scope.defer;
        }
      , transclude: true
      , template: '<shimmy-content closeable="{{closeable}}" shimmy-content-class="{{shimmyContentClass}}" content-template="{{contentTemplate}}" content-template-url="{{contentTemplateUrl}}"></shimmy-content>'
      , link: function(scope, elem, attrs, ctrl) {
            ctrl.resolve(elem);

            if(attrs.closeable) {
                elem.on('click', function() {
                    document.body.removeChild(elem[0]);
                });
            }
        }
    };
})

.directive('shimmyContent', function($compile, $timeout, $templateCache) {
    return {
        restrict: 'E'
      , scope: {
            contentTemplate:    '@'
          , contentTemplateUrl: '@'
          , shimmyContentClass: '@'
          , closeable:          '@'
        }
      , require: '^shimmy'
      , tranclude: true
      , link: function(scope, elem, attrs) {
            var template = attrs.contentTemplate || $templateCache.get(attrs.contentTemplateUrl) || $templateCache.get(template($templateCache, attrs.contentTemplateUrl)) || '';

            if(attrs.closeable) {
                elem.append($compile('<shimmy-close></shimmy-close>')(scope));
            }
            elem.append($compile(template)(scope));
            elem.addClass(attrs.shimmyContentClass);
            $timeout(function() {
                var css = center(elem);
                elem.css(css);
                elem.on('click', function(e) {
                    e.stopPropagation();
                });
            });
        }
    };
})

.directive('shimmyClose', function() {
    return {
        restrict: 'E'
      , translude: true
      , template: '×'
      , link: function(scope, elem, attrs, ctrl) {
            scope.$watch('$parent', function(nv, ov) {
                if(nv && nv.defer) {
                    nv.defer.promise.then(function(shimmy) {
                        elem.on('click', function() {
                            document.body.removeChild(shimmy[0]);
                        });
                    });
                }
            });
        }
    };
});

/*
    <shimmy-button shimmy-class="foo" shimmy-content-class="shim-test-class" content-template-url="foo.html">Shimmy on over</shimmy-button>

*/<|endoftext|>


<commit_before>const state = {
  IDLE: 'idle',
  LISTENING: 'listening'
};

class Speech {
  init(cb) {
    const grammar = '#JSGF V1.0; grammar share; public <share> = share | snap | post | cancel;'
    const speechRecognitionList = new webkitSpeechGrammarList();

    this.state = state.IDLE;
    this.recognition = new webkitSpeechRecognition();
    speechRecognitionList.addFromString(grammar, 1);
    this.recognition.grammars = speechRecognitionList;

    this.recognition.onend = () => {
      if(this.state === state.LISTENING){
        this.recognition.start();
      }
    }

    this.recognition.onresult = (event) => {
      const result = event.results[0][0].transcript;

      console.log(`DID YOU SAY ${result}?`);

      switch(result) {
        case 'cancel':
        case 'snap':
          this.state = state.IDLE;
          this.recognition.stop();
          cb(result);
          break;
      }
    }
  }

  listen(cb) {
    if(!this.recognition) this.init(cb);
    this.recognition.start();
    this.state = state.LISTENING;
    console.log('LISTENING');
  }

  stop() {
    this.state = state.IDLE;
    this.recognition.stop();
  }
}
<commit_msg>dima's speech improvements<commit_after>const state = {
  IDLE: 'idle',
  LISTENING: 'listening'
};

function LevenshteinDistance(s, t) {
  const sLength = s.length;
  const tLength = t.length;
  const matrix = [];

  if(sLength === 0) return tLength;
  if(tLength === 0) return sLength;

  for(let i = 0; i <= sLength; matrix[i] = [i++]){}
  for(let j = 0; j <= tLength; matrix[0][j] = j++){}

  for(let i = 1; i <= sLength; i++){
    for(let j = 1; j <= tLength; j++){
      const cost = t[j - 1] === s[i - 1] ? 0 : 1;
      const currMin = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1);

      matrix[i][j] = Math.min(currMin, matrix[i - 1][j - 1] + cost);
    }
  }

  return matrix[sLength][tLength];
}

const commands = ['snap', 'cancel', 'post'];

class Speech {
  init(cb) {
    const grammar = '#JSGF V1.0; grammar share; public <share> = share | snap | post | cancel;'
    const speechRecognitionList = new webkitSpeechGrammarList();

    this.state = state.IDLE;
    this.recognition = new webkitSpeechRecognition();
    speechRecognitionList.addFromString(grammar, 1);
    this.recognition.grammars = speechRecognitionList;

    this.recognition.onend = () => {
      if(this.state === state.LISTENING){
        this.recognition.start();
      }
    }

    this.recognition.onresult = (event) => {
      const result = event.results[0][0].transcript;
      const words = result.split(' ');
      console.log(`DID YOU SAY ${result}?`);

      const match = words.map(word => {
        if(commands.indexOf(word) > -1){
          return word;
        }

        return commands
          .map(command => LevenshteinDistance(command, word) < 2 ? command : null)
          .filter(a => a).pop();
      }).filter(a => a).pop();

      switch(match) {
        case 'cancel':
        case 'snap':
          this.state = state.IDLE;
          this.recognition.stop();
          cb(match);
          break;
      }
    }
  }

  listen(cb) {
    if(!this.recognition) this.init(cb);
    this.recognition.start();
    this.state = state.LISTENING;
    console.log('LISTENING');
  }

  stop() {
    this.state = state.IDLE;
    this.recognition.stop();
  }
}
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var spritesmith = require('spritesmith');
var templater = require('spritesheet-templates');
//自定义template
templater.addTemplate('sprite', require(path.join(__dirname, 'templates/sprite.js')));

var util = require('./util');

function CreateSprite(options){}
var createProto = CreateSprite.prototype;

createProto.run = function(options){
    this.initOptions(options);
    var self = this;
    var items = fs.readdirSync(self.aFolder);
    var files = items.filter(function (item) {
        var sName = item.split('.')[0]; if(!sName) return false;
        var tName = self.tSpriteFile.split("/").pop().split(".")[0];
        return self.aExtname.indexOf(path.extname(item))>-1&&(item)&&sName!=tName;
    }).map(function (item) {
        return self.aFolder + '/' + item;
    });

    this.createSpriteImage(files);
};
createProto.initOptions = function(opt){
    var dconfig = {};
    try {
        var CONFIG_PATH = process.argv[2].split("=")[1];
        dconfig = require(CONFIG_PATH)
    } catch (e) {
        util.info(e.message+"!use the default config to create sprite");
    }

    var config = Object.assign(require('./config'),dconfig, opt);
    var targetOptions = {
        styleFile: config.styleFile,
        spriteFile: config.spriteFile,
        fix4Pieces: config.fix4Pieces,
        styleTemplate: {
            format: 'sprite',
            pieces: config.pieces,
            formatOpts: {
                'cssClass': config.prefix,
                'connector': config.connector,
                'processor': config.processor
            }
        },
    };
    var options = {
        aPadding: config.aPadding,
        aExtname: config.aExtname,
        aAlgorithm: config.aAlgorithm,
        aFolder: config.aFolder,
        target: targetOptions
    };
    this.initConstructor(options);
}
createProto.initConstructor = function(options){

    this.aFolder = options.aFolder;
    this.aPadding = options.aPadding;
    this.aAlgorithm = options.aAlgorithm;
    this.aExtname = options.aExtname || '.png|.jpg';

    this.tStyleFile = options.target.styleFile;
    this.tSpriteFile = options.target.spriteFile;
    this.tStyleTemplate = options.target.styleTemplate;

    this.fix4Pieces = options.target.fix4Pieces;

    this.formatFileName = options.formatFileName || function(filename) {
        return filename.replace('@', ':');
    };
    console.log("\nstart\n");

    util.info("assets:", "folder: " + this.aFolder)
    util.info("target:", "styleFile: " + this.tStyleFile)
    util.info("target:", "spriteFile: " + this.tSpriteFile)
    console.log();
}
createProto.createSpriteImage = function(files){
    var self = this;
    if(fs.existsSync(self.tSpriteFile)) fs.unlinkSync(self.tSpriteFile);
    spritesmith.run(Object.assign({src: files},{algorithm: this.aAlgorithm},{padding: this.aPadding}), function(err, result) {
        util.info('spriteFile:', JSON.stringify(result.properties));
        console.log()

        for (var k in result.coordinates) {
            util.info(path.basename(k)+":", JSON.stringify(result.coordinates[k]));
        }

        fs.writeFileSync(self.tSpriteFile, result.image);

        var imagemin = require('imagemin');
        var imageminWebp = require('imagemin-webp');
        var imageminGifsicle = require('imagemin-gifsicle');
        var imageminMozjpeg = require('imagemin-mozjpeg');
        var imageminOptipng = require('imagemin-optipng');
        var imageminSvgo = require('imagemin-svgo');
        var imageminPngquant = require('imagemin-pngquant');
        imagemin([self.tSpriteFile], path.join(self.tSpriteFile,"../"), {
            use: [
                imageminGifsicle({
                    interlaced: false
                }),
                imageminMozjpeg(),
                imageminSvgo(),
                imageminPngquant(),
                imageminOptipng()
            ]
        }).then(function() {
            console.log()
            console.log('Images optimized');
            util.info("create:", "spriteFile: "+self.tSpriteFile)

            self.createSpriteStyle(result);
        })

    });
};
createProto.createSpriteStyle = function(result){
    var self = this;
    var styles = templater({
        sprites: Object.keys(result.coordinates).map(function(key) {
            return {
                name: self.formatFileName(path.basename(key).split('.')[0]),
                x: result.coordinates[key].x,
                y: result.coordinates[key].y,
                width: result.coordinates[key].width,
                height: result.coordinates[key].height,
            }
        }),
        spritesheet: {
            image: path.relative(path.join(self.tStyleFile, "../"), self.tSpriteFile),
            width: result.properties.width,
            height: result.properties.height
        }
    }, self.tStyleTemplate);
    fs.writeFileSync(self.tStyleFile, this.fix4Pieces+ "\n" + styles);
    util.info('create:', "styleFile: " + self.tStyleFile);
    console.log("\nsuccess\n");
}
var spriteInstance = new CreateSprite();

// spriteInstance.run({
//     aPadding: config.aPadding,
//     aExtname: config.aExtname,
//     aAlgorithm: config.aAlgorithm,
//     aFolder: config.aFolder,
//     target: targetOptions
// });

module.exports = spriteInstance
<commit_msg>Update sprite.js<commit_after>var fs = require('fs');
var path = require('path');
var spritesmith = require('spritesmith');
var templater = require('spritesheet-templates');
//自定义template
templater.addTemplate('sprite', require(path.join(__dirname, 'templates/sprite.js')));

var util = require('./util');

function CreateSprite(options){}
var createProto = CreateSprite.prototype;

createProto.run = function(options){
    this.initOptions(options);
    var self = this;
    var items = fs.readdirSync(self.aFolder);
    var files = items.filter(function (item) {
        var sName = item.split('.')[0]; if(!sName) return false;
        var tName = self.tSpriteFile.split("/").pop().split(".")[0];
        return self.aExtname.indexOf(path.extname(item))>-1&&(item)&&sName!=tName;
    }).map(function (item) {
        return self.aFolder + '/' + item;
    });

    this.createSpriteImage(files);
};
createProto.initOptions = function(opt){
    var dconfig = {};
    try {
        var CONFIG_PATH = process.argv[2].split("=")[1];
        dconfig = require(CONFIG_PATH)
    } catch (e) {
        util.info("warning: ", e.message+"!use the default config to create sprite");
    }

    var config = Object.assign(require('./config'),dconfig, opt);
    var targetOptions = {
        styleFile: config.styleFile,
        spriteFile: config.spriteFile,
        fix4Pieces: config.fix4Pieces,
        styleTemplate: {
            format: 'sprite',
            pieces: config.pieces,
            formatOpts: {
                'cssClass': config.prefix,
                'connector': config.connector,
                'processor': config.processor
            }
        },
    };
    var options = {
        aPadding: config.aPadding,
        aExtname: config.aExtname,
        aAlgorithm: config.aAlgorithm,
        aFolder: config.aFolder,
        target: targetOptions
    };
    this.initConstructor(options);
}
createProto.initConstructor = function(options){

    this.aFolder = options.aFolder;
    this.aPadding = options.aPadding;
    this.aAlgorithm = options.aAlgorithm;
    this.aExtname = options.aExtname || '.png|.jpg';

    this.tStyleFile = options.target.styleFile;
    this.tSpriteFile = options.target.spriteFile;
    this.tStyleTemplate = options.target.styleTemplate;

    this.fix4Pieces = options.target.fix4Pieces;

    this.formatFileName = options.formatFileName || function(filename) {
        return filename.replace('@', ':');
    };
    console.log("\nstart\n");

    util.info("assets:", "folder: " + this.aFolder)
    util.info("target:", "styleFile: " + this.tStyleFile)
    util.info("target:", "spriteFile: " + this.tSpriteFile)
    console.log();
}
createProto.createSpriteImage = function(files){
    var self = this;
    if(fs.existsSync(self.tSpriteFile)) fs.unlinkSync(self.tSpriteFile);
    spritesmith.run(Object.assign({src: files},{algorithm: this.aAlgorithm},{padding: this.aPadding}), function(err, result) {
        util.info('spriteFile:', JSON.stringify(result.properties));
        console.log()

        for (var k in result.coordinates) {
            util.info(path.basename(k)+":", JSON.stringify(result.coordinates[k]));
        }

        fs.writeFileSync(self.tSpriteFile, result.image);

        var imagemin = require('imagemin');
        var imageminWebp = require('imagemin-webp');
        var imageminGifsicle = require('imagemin-gifsicle');
        var imageminMozjpeg = require('imagemin-mozjpeg');
        var imageminOptipng = require('imagemin-optipng');
        var imageminSvgo = require('imagemin-svgo');
        var imageminPngquant = require('imagemin-pngquant');
        imagemin([self.tSpriteFile], path.join(self.tSpriteFile,"../"), {
            use: [
                imageminGifsicle({
                    interlaced: false
                }),
                imageminMozjpeg(),
                imageminSvgo(),
                imageminPngquant(),
                imageminOptipng()
            ]
        }).then(function() {
            console.log()
            console.log('Images optimized');
            util.info("create:", "spriteFile: "+self.tSpriteFile)

            self.createSpriteStyle(result);
        })

    });
};
createProto.createSpriteStyle = function(result){
    var self = this;
    var styles = templater({
        sprites: Object.keys(result.coordinates).map(function(key) {
            return {
                name: self.formatFileName(path.basename(key).split('.')[0]),
                x: result.coordinates[key].x,
                y: result.coordinates[key].y,
                width: result.coordinates[key].width,
                height: result.coordinates[key].height,
            }
        }),
        spritesheet: {
            image: path.relative(path.join(self.tStyleFile, "../"), self.tSpriteFile),
            width: result.properties.width,
            height: result.properties.height
        }
    }, self.tStyleTemplate);
    fs.writeFileSync(self.tStyleFile, this.fix4Pieces+ "\n" + styles);
    util.info('create:', "styleFile: " + self.tStyleFile);
    console.log("\nsuccess\n");
}
var spriteInstance = new CreateSprite();

// spriteInstance.run({
//     aPadding: config.aPadding,
//     aExtname: config.aExtname,
//     aAlgorithm: config.aAlgorithm,
//     aFolder: config.aFolder,
//     target: targetOptions
// });

module.exports = spriteInstance
<|endoftext|>


<commit_before>import React from 'react';

export default class Square extends React.Component {
    constructor() {
        super();

        this.move.bind(this);
    }
    move(direction) {
        console.log('Square moved:', direction)
    }
    render() {
        return (

                <button
                    onClick={this.move('up')}
                    className="square__arrow--up">↑</button>
                <button
                    onClick={this.move('right')}
                    className="square__arrow--right">→</button>
                <button
                    onClick={this.move('down')}
                    className="square__arrow--down">↓</button>
                <button
                    onClick={this.move('left')}
                    className="square__arrow--left">←</button>

        );
    }
};
<commit_msg>Wire up square buttons to 'move' method :baby:<commit_after>import React from 'react';

export default class Square extends React.Component {
    move(direction) {
        console.log('Square moved:', direction)
    }
    render() {
        return (

                <button
                    onClick={this.move.bind(this, 'up')}
                    className="square__arrow--up">↑</button>
                <button
                    onClick={this.move.bind(this, 'right')}
                    className="square__arrow--right">→</button>
                <button
                    onClick={this.move.bind(this, 'down')}
                    className="square__arrow--down">↓</button>
                <button
                    onClick={this.move.bind(this, 'left')}
                    className="square__arrow--left">←</button>

        );
    }
};
<|endoftext|>


<commit_before>// utilities to retrieve the grid capabilities.
var http = require('http'),
    htmlparser = require('htmlparser');

exports.available = function(options, callback) {
    http.get({
        host: options.host,
        path: '/grid/console',
        port: options.port || 4444
    }, function(res) {
        var data = '';

        if (res.statusCode !== 200) {
            return callback('server returned status code ' + res.statusCode);
        }

        res.on('data', function(c) {
            data += c;
        });

        res
            .on('end', function () { parseHtml(data, callback); })
            .on('error', callback);
    });
};

exports.parseHtml = function parseHtml(data, callback) {
    var parser,
        handler = new htmlparser.DefaultHandler(function (error, dom) {
            if (error) {return callback(error);}
            var container = find(matchBrowserList, dom),
                browsers = find(matchBrowserItem, container),
                parsed = parseItems(browsers);
            callback(null, parsed);
        }, { verbose: false, ignoreWhitespace: true });

    parser = new htmlparser.Parser(handler);
    parser.parseComplete(data);
};

var regContent = /{\s*([^\s].*[^\s])\s*}/;
var regSplit = /\s*,\s*/;
var regPart = /^(.*)\s*=\s*(.*)$/;

function find(match, dom) {
    var children = [], c;
    if (Array.isArray(dom)) {
        dom.forEach(function(item) {
            var c = find(match, item);
            if (c.length) {
                c.forEach(function(i) {
                    children.push(i);
                });
            }
        });
    } else {
        if (dom.type === "tag" && match(dom)) {
            children.push(dom);
        }

        if (dom.children) {
            c = find(match, dom.children);
            if (c.length) {
                c.forEach(function(i) {
                    children.push(i);
                });
            }
        }
    }

    return children;
}

function matchBrowserList(node) {
    return node.name === 'div' && node.attribs && node.attribs.type === 'browsers';
}

function matchBrowserItem(node) {
    return node.name === 'img';
}

function parseItems(items) {
    var browsers = [];
    items.forEach(function (item) {
        var result = parseItem(item.attribs.title);
        if (result) {
            browsers.push(result);
        }
    });
    return browsers;
}

function parseItem(title) {
    if (!regContent.test(title)) {
        // this is not an available node
        return false;
    }
    var raw = regContent.exec(title)[1];
    var parts = raw.split(regSplit);
    var result = {};
    parts.forEach(function (part) {
        var parsed = regPart.exec(part);
        var key = parsed[1];
        var value = parsed[2];

        result[key] = value;
    });

    return result;
}
<commit_msg>Fix: Exported function wasn't usable within status module.<commit_after>// utilities to retrieve the grid capabilities.
var http = require('http'),
    htmlparser = require('htmlparser');

exports.available = function(options, callback) {
    http.get({
        host: options.host,
        path: '/grid/console',
        port: options.port || 4444
    }, function(res) {
        var data = '';

        if (res.statusCode !== 200) {
            return callback('server returned status code ' + res.statusCode);
        }

        res.on('data', function(c) {
            data += c;
        });

        res
            .on('end', function () { parseHtml(data, callback); })
            .on('error', callback);
    });
};

exports.parseHtml = parseHtml;

function parseHtml(data, callback) {
    var parser,
        handler = new htmlparser.DefaultHandler(function (error, dom) {
            if (error) {return callback(error);}
            var container = find(matchBrowserList, dom),
                browsers = find(matchBrowserItem, container),
                parsed = parseItems(browsers);
            callback(null, parsed);
        }, { verbose: false, ignoreWhitespace: true });

    parser = new htmlparser.Parser(handler);
    parser.parseComplete(data);
}

var regContent = /{\s*([^\s].*[^\s])\s*}/;
var regSplit = /\s*,\s*/;
var regPart = /^(.*)\s*=\s*(.*)$/;

function find(match, dom) {
    var children = [], c;
    if (Array.isArray(dom)) {
        dom.forEach(function(item) {
            var c = find(match, item);
            if (c.length) {
                c.forEach(function(i) {
                    children.push(i);
                });
            }
        });
    } else {
        if (dom.type === "tag" && match(dom)) {
            children.push(dom);
        }

        if (dom.children) {
            c = find(match, dom.children);
            if (c.length) {
                c.forEach(function(i) {
                    children.push(i);
                });
            }
        }
    }

    return children;
}

function matchBrowserList(node) {
    return node.name === 'div' && node.attribs && node.attribs.type === 'browsers';
}

function matchBrowserItem(node) {
    return node.name === 'img';
}

function parseItems(items) {
    var browsers = [];
    items.forEach(function (item) {
        var result = parseItem(item.attribs.title);
        if (result) {
            browsers.push(result);
        }
    });
    return browsers;
}

function parseItem(title) {
    if (!regContent.test(title)) {
        // this is not an available node
        return false;
    }
    var raw = regContent.exec(title)[1];
    var parts = raw.split(regSplit);
    var result = {};
    parts.forEach(function (part) {
        var parsed = regPart.exec(part);
        var key = parsed[1];
        var value = parsed[2];

        result[key] = value;
    });

    return result;
}
<|endoftext|>


<commit_before>/**
 * Describe a synchronous benchmark
 */
export type SynchronousBenchmark =
{
  name: string;
  run: () => mixed;
}

/**
 * Describe an asynchronous benchmark
 */
export type ASynchronousBenchmark =
{
  name: string;
  startRunning : () => Promise;
}

/**
 * Supported benchmarking styles
 */
export type Benchmark = SynchronousBenchmark | ASynchronousBenchmark;

/**
 * Result of running one benchmark
 */
export type BenchmarkResult =
{
  name: string;
  opsPerSample: number;
  numSamples: number;
  timingSamples: Array<number>;
  memorySamples: Array<number>;
}

/**
 * Options controlling how benchmarks are run
 */
export type BenchmarkingOptions =
{
}

/**
 * Result of running a suite of benchmarks
 */
export type BenchmarkSuiteResult =
{
  name: string;
  options: BenchmarkingOptions;
  startTime: Array<number>;
  results: Array<BenchmarkResult>;
}

/**
 * Start the process of running a suite of benchmarks
 */
export function startBenchmarking(
  name: String,
  benchmarkSuite: Array<Benchmark>,
  options: BenchmarkingOptions
) : Promise {
  return new Promise((resolve: resolveFn, reject: rejectFn) => {
    const suiteResult : BenchmarkSuiteResult = {
      name,
      options,
      startTime: Date.now(),
      results: []
    };
    scheduleBenchmark(resolve, reject, benchmarkSuite, suiteResult);
  });
}

type resolveFn = (value:mixed) => void;

type rejectFn = (value:mixed) => void;

/**
 * Schedule a benchmark to be run at a later time
 */
function scheduleBenchmark(
  resolve: resolveFn,
  reject: rejectFn,
  benchmarkSuite: Array<Benchmark>,
  suiteResult: BenchmarkSuiteResult
) : void {
  setImmediate(runBenchmark, resolve, reject, benchmarkSuite, suiteResult);
}

/**
 * Start the process of running a single benchmark
 */
function runBenchmark(
  resolve: resolveFn,
  reject: rejectFn,
  benchmarkSuite: Array<Benchmark>,
  suiteResult: BenchmarkSuiteResult
) : void {

  const benchmark = benchmarkSuite.shift();

  // if there are no more benchmarks, stop
  if (!benchmark) {
    resolve(suiteResult);
    return;
  }

  if (!benchmark.name) {
    reject(new Error(
    `"${benchmark.name} does not provide a name`
    ));
  }

  const initialResult:BenchmarkResult = {
    name: benchmark.name,
    opsPerSample: 100,
    numSamples: 20,
    timingSamples: [],
    memorySamples: []
  };

  const resolveBenchmark = (finalResult: BenchmarkResult) => {
    suiteResult.results.push(finalResult);
    scheduleBenchmark(resolve, reject, benchmarkSuite, suiteResult);
  };

  scheduleSample(resolveBenchmark, reject, benchmark, initialResult);
}

/**
 * Schedule sample collection
 */
function scheduleSample(
  resolve: resolveFn,
  reject: rejectFn,
  benchmark: ASynchronousBenchmark,
  result: BenchmarkResult
) {
  if (benchmark.run) {
    setImmediate(
      collectSynchronousSample,
      resolve,
      reject,
      benchmark,
      result
    );
  } else if (benchmark.startRunning) {
    setImmediate(
      collectAsynchronousSample,
      resolve,
      reject,
      benchmark,
      result
    );
  } else {
    reject(new Error(
    `"${benchmark.name} does not provide a run or startRunning function`
    ));
  }
}

/**
 * Run an asynchronous benchmark for the desired number of operations and capture
 * the results
 */
function collectAsynchronousSample(
  resolve: resolveFn,
  reject: rejectFn,
  benchmark: ASynchronousBenchmark,
  result: BenchmarkResult
) {
  cleanUpMemory();

  const promises = new Array(result.opsPerSample);

  const startMemory = getStartMemory();
  const startTime = getStartTime();

  // run the benchmark function and collect promises
  for (let i = 0; i < result.opsPerSample; i++) {
    promises[i] = benchmark.startRunning();
  }

  // take measurements when all promises have been resolved
  Promise.all(promises).then(() => {

    const elapsedTime = getTimeElapsed(startTime);
    const memoryUsed = getMemoryUsed(startMemory);

    recordSample(result, elapsedTime, memoryUsed);

    if (isSamplingComplete(result)) {
      resolve(result);
    } else {
      scheduleSample(resolve, reject, benchmark, result);
    }
  });
}

/**
 * Run a synchronous benchmark for the desired number of operations and capture
 * the results
 */
function collectSynchronousSample(
  resolve: resolveFn,
  reject: rejectFn,
  benchmark: SynchronousBenchmark,
  result: BenchmarkResult
) {
  cleanUpMemory();

  const startMemory = getStartMemory();
  const startTime = getStartTime();

  // run the benchmark function
  for (let i = 0; i < result.opsPerSample; i++) {
    benchmark.run();
  }

  const elapsedTime = getTimeElapsed(startTime);
  const memoryUsed = getMemoryUsed(startMemory);

  recordSample(result, elapsedTime, memoryUsed);

  if (isSamplingComplete(result)) {
    resolve(result);
  } else {
    scheduleSample(resolve, reject, benchmark, result);
  }
}

/**
 * Does the result contain all required samples?
 */
function isSamplingComplete(result: BenchmarkResult) {
  return result.timingSamples.length >= result.numSamples;
}

/**
 * Record a sample memory and time into a benchmark result
 */
function recordSample(benchmarkResult, elapsedTime, memoryUsed) {
  benchmarkResult.timingSamples.push(elapsedTime);
  benchmarkResult.memorySamples.push(memoryUsed);
}

/**
 * Read the current time, using the highest resolution timer at our disposal
 */
function getStartTime() {
  return process.hrtime();
}

/**
 * Convert an array from process.hrtime into an integer microseconds number
 */
function convertHrTimeToMicroseconds(hrTime) {
  const microsecondsPerSecond = 1000000;
  const microsecondsPerNanoSecond = 1000;
  return Math.round(
    hrTime[0] * microsecondsPerSecond + hrTime[1] / microsecondsPerNanoSecond
  );
}

/**
 * Determine how much time has passed in microseconds since an initial reading was taken
 */
function getTimeElapsed(startTime) {
  const elapsed = process.hrtime(startTime);
  return convertHrTimeToMicroseconds(elapsed);
}

/**
 * Read the current memory usage
 */
function getStartMemory() {
  return process.memoryUsage();
}

/**
 * Determine how much memory has been used since an initial reading was taken
 */
function getMemoryUsed(startMemory) {
  const memory = process.memoryUsage();
  return memory.heapUsed - startMemory.heapUsed;
}

/**
 * Put memory into a stable state if we can to avoid triggering gc during a
 * sample run
 */
function cleanUpMemory() {
  if (global.gc) {
    global.gc();
  }
}
<commit_msg>Add setUp and tearDown functions for benchmarks<commit_after>/**
 * Describe a synchronous benchmark
 */
export type SynchronousBenchmark =
{
  name: string;
  setUp?: () => void;
  tearDown?: () => void;
  run: () => mixed;
}

/**
 * Describe an asynchronous benchmark
 */
export type ASynchronousBenchmark =
{
  name: string;
  setUp?: () => void;
  tearDown?: () => void;
  startRunning : () => Promise;
}

/**
 * Supported benchmarking styles
 */
export type Benchmark = SynchronousBenchmark | ASynchronousBenchmark;

/**
 * Result of running one benchmark
 */
export type BenchmarkResult =
{
  name: string;
  opsPerSample: number;
  numSamples: number;
  timingSamples: Array<number>;
  memorySamples: Array<number>;
}

/**
 * Options controlling how benchmarks are run
 */
export type BenchmarkingOptions =
{
}

/**
 * Result of running a suite of benchmarks
 */
export type BenchmarkSuiteResult =
{
  name: string;
  options: BenchmarkingOptions;
  startTime: Array<number>;
  results: Array<BenchmarkResult>;
}

/**
 * Start the process of running a suite of benchmarks
 */
export function startBenchmarking(
  name: String,
  benchmarkSuite: Array<Benchmark>,
  options: BenchmarkingOptions
) : Promise {
  return new Promise((resolve: resolveFn, reject: rejectFn) => {
    const suiteResult : BenchmarkSuiteResult = {
      name,
      options,
      startTime: Date.now(),
      results: []
    };
    scheduleNextBenchmark(resolve, reject, benchmarkSuite, suiteResult);
  });
}

type resolveFn = (value:mixed) => void;

type rejectFn = (value:mixed) => void;

/**
 * Schedule a benchmark to be run at a later time
 */
function scheduleNextBenchmark(
  resolve: resolveFn,
  reject: rejectFn,
  benchmarkSuite: Array<Benchmark>,
  suiteResult: BenchmarkSuiteResult
) : void {
  setImmediate(runBenchmark, resolve, reject, benchmarkSuite, suiteResult);
}

/**
 * Start the process of running a single benchmark
 */
function runBenchmark(
  resolve: resolveFn,
  reject: rejectFn,
  benchmarkSuite: Array<Benchmark>,
  suiteResult: BenchmarkSuiteResult
) : void {

  const benchmark = benchmarkSuite.shift();

  // if there are no more benchmarks, stop
  if (!benchmark) {
    resolve(suiteResult);
    return;
  }

  if (!benchmark.name) {
    reject(new Error(
    `"${benchmark.name} does not provide a name`
    ));
    return;
  }

  const initialResult:BenchmarkResult = {
    name: benchmark.name,
    opsPerSample: 100,
    numSamples: 20,
    timingSamples: [],
    memorySamples: []
  };

  const Err = setUpBenchmark(benchmark);
  if (Err) {
    reject(Err);
    return;
  }

  const resolveBenchmark = (finalResult: BenchmarkResult) => {
    suiteResult.results.push(finalResult);
    scheduleNextBenchmark(resolve, reject, benchmarkSuite, suiteResult);
  };

  scheduleNextSample(resolveBenchmark, reject, benchmark, initialResult);
}

/**
 *  Setup benchmark resources before running
 */
function setupBenchmark(benchmark: Benchmark): Error | false {
  if (benchmark.setUp) {
    try {
      benchmark.setUp();
    } catch (e) {
      return e;
    }
  }
  return false;
}

/**
 * Schedule sample collection
 */
function scheduleNextSample(
  resolve: resolveFn,
  reject: rejectFn,
  benchmark: ASynchronousBenchmark,
  result: BenchmarkResult
): void {
  if (isSamplingComplete(result)) {
    const Err = tearDownBenchmark(benchmark);
    if (Err) {
      reject(Err);
    } else {
      resolve(result);
    }
    return;
  }
  if (benchmark.run) {
    setImmediate(
      collectSynchronousSample,
      resolve,
      reject,
      benchmark,
      result
    );
  } else if (benchmark.startRunning) {
    setImmediate(
      collectAsynchronousSample,
      resolve,
      reject,
      benchmark,
      result
    );
  } else {
    reject(new Error(
    `"${benchmark.name} does not provide a run or startRunning function`
    ));
  }
}

/**
 * Does the result contain all required samples?
 */
function isSamplingComplete(result: BenchmarkResult) {
  return result.timingSamples.length >= result.numSamples;
}

/**
 *  release benchmark resources after running
 */
function tearDownBenchmark(benchmark: Benchmark): Error | false {
  if (benchmark.tearDown) {
    try {
      benchmark.tearDown();
    } catch (e) {
      return e;
    }
  }
  return false;
}

/**
 * Run an asynchronous benchmark for the desired number of operations and capture
 * the results
 */
function collectAsynchronousSample(
  resolve: resolveFn,
  reject: rejectFn,
  benchmark: ASynchronousBenchmark,
  result: BenchmarkResult
): void {
  const promises = new Array(result.opsPerSample);

  cleanUpMemory();

  const startMemory = getStartMemory();
  const startTime = getStartTime();

  // run the benchmark function and collect promises
  for (let i = 0; i < result.opsPerSample; i++) {
    promises[i] = benchmark.startRunning();
  }

  // take measurements when all promises have been resolved
  Promise.all(promises).then(() => {

    const elapsedTime = getTimeElapsed(startTime);
    const memoryUsed = getMemoryUsed(startMemory);

    recordSample(result, elapsedTime, memoryUsed);

    scheduleNextSample(resolve, reject, benchmark, result);
  });
}

/**
 * Run a synchronous benchmark for the desired number of operations and capture
 * the results
 */
function collectSynchronousSample(
  resolve: resolveFn,
  reject: rejectFn,
  benchmark: SynchronousBenchmark,
  result: BenchmarkResult
) {
  cleanUpMemory();

  const startMemory = getStartMemory();
  const startTime = getStartTime();

  // run the benchmark function
  for (let i = 0; i < result.opsPerSample; i++) {
    benchmark.run();
  }

  const elapsedTime = getTimeElapsed(startTime);
  const memoryUsed = getMemoryUsed(startMemory);

  recordSample(result, elapsedTime, memoryUsed);

  scheduleNextSample(resolve, reject, benchmark, result);
}

/**
 * Record a sample memory and time into a benchmark result
 */
function recordSample(benchmarkResult, elapsedTime, memoryUsed) {
  benchmarkResult.timingSamples.push(elapsedTime);
  benchmarkResult.memorySamples.push(memoryUsed);
}

/**
 * Read the current time, using the highest resolution timer at our disposal
 */
function getStartTime() {
  return process.hrtime();
}

/**
 * Convert an array from process.hrtime into an integer microseconds number
 */
function convertHrTimeToMicroseconds(hrTime) {
  const microsecondsPerSecond = 1000000;
  const microsecondsPerNanoSecond = 1000;
  return Math.round(
    hrTime[0] * microsecondsPerSecond + hrTime[1] / microsecondsPerNanoSecond
  );
}

/**
 * Determine how much time has passed in microseconds since an initial reading was taken
 */
function getTimeElapsed(startTime) {
  const elapsed = process.hrtime(startTime);
  return convertHrTimeToMicroseconds(elapsed);
}

/**
 * Read the current memory usage
 */
function getStartMemory() {
  return process.memoryUsage();
}

/**
 * Determine how much memory has been used since an initial reading was taken
 */
function getMemoryUsed(startMemory) {
  const memory = process.memoryUsage();
  return memory.heapUsed - startMemory.heapUsed;
}

/**
 * Put memory into a stable state if we can to avoid triggering gc during a
 * sample run
 */
function cleanUpMemory() {
  if (global.gc) {
    global.gc();
  }
}
<|endoftext|>


<commit_before>'use strict';

const TI = require('../../');
const is = TI.is;
const ElementArea = TI.PDF.Element.Area;
const Layout = TI.PDF.Layout;

/**
 * Base class for printable elements
 */
class PDFElement {
	/**
	 * All dimensions in inches
	 * @param {String} [style] pdf-style.json rule
	 */
	constructor(style) {
		/**
		 * Right-side distance from containing area
		 * @type {Number}
		 */
		this.right = NaN;
		/**
		 * Bottom distance from containing area
		 * @type {Number}
		 */
		this.bottom = NaN;
		/** @type {ElementArea} */
		this.area = new ElementArea();
		/**
		 * Element dimensions relative to PDF page
		 * @type {ElementArea}
		 */
		this.absolute = new ElementArea();
		/** @type {Number} */
		this.minWidth = NaN;
		/** @type {Number} */
		this.minHeight = NaN;
		/** @type {String} */
		this.alignContent = Layout.Align.Left;
		/** @type {String} */
		this.verticalAlign = Layout.Align.Top;
		/**
		 * How to scale element
		 * @type {String}
		 */
		this.scaleTo = Layout.Scale.None;

		/**
		 * Style name to match in pdf-style.json rules
		 * @type {String}
		 */
		this.style = style;

		/**
		 * RGB color
		 * @type {Number[]}
		 */
		this._color = [0, 0, 0, 1];

		/**
		 * RGBA color
		 * @type {Number[]}
		 */
		this.backgroundColor = [];

		/** @type {Number} */
		this.zIndex = 0;
	}

	/** @param {Number} n */
	set width(n) { this.area.width = n; }
	/** @return {Number} */
	get width() { return this.area.width; }

	/** @param {Number} n */
	set height(n) { this.area.height = n; }
	/** @return {Number} */
	get height() { return this.area.height; }

	/** @param {Number} n */
	set left(n) { this.area.left = n; }
	/** @return {Number} */
	get left() { return this.area.left; }

	/** @param {Number} n */
	set top(n) { this.area.top = n; }
	/** @return {Number} */
	get top() { return this.area.top; }

	/**
	 * @returns {Number[]}
	 */
	get color() {
		if (is.array(this._color)) {
			return (this._color.length > 3) ? this._color.slice(0, 3) : this._color;
		} else {
			return [0, 0, 0];
		}
	}

	/**
	 * @param {Number[]} c
	 */
	set color(c) { this._color = c; }

	/**
	 *
	 * @returns {Number}
	 */
	get opacity() {
		return (is.array(this._color) && this._color.length > 3) ? this._color[3] : 1;
	}

	/**
	 * @param {Number} a
	 */
	set opacity(a) {
		if (is.array(this._color)) {
			this._color[3] = a;
		} else {
			this._color = [0, 0, 0, a];
		}
	}

	/**
	 * Whether full layout is defined for PDF rendering
	 * @returns {Boolean}
	 */
	get laidOut() { return !this.area.isEmpty; }

	/**
	 * Set width and height for elements with anchored opposite edges
	 * Previously set width and height will be replaced
	 * @param {ElementArea} maxArea
	 */
	scale(maxArea) {
		if (!isNaN(this.left) && !isNaN(this.right)) {
			// adjust width to fit left and right parameters
			this.width = maxArea.width - (this.left + this.right);
		}

		if (!isNaN(this.top) && !isNaN(this.bottom)) {
			// adjust height to fit top and bottom parameters
			this.height = maxArea.height - (this.top + this.bottom);
		}
	}

	/**
	 * Position elements that are anchored to only one edge
	 * @param {ElementArea} area
	 */
	positionWithin(area) {
		if (!isNaN(this.bottom) && !isNaN(this.height) && isNaN(this.top)) {
			// position relative to bottom
			this.top = area.height - (this.height + this.bottom);
		}

		if (!isNaN(this.right) && !isNaN(this.width) && isNaN(this.left)) {
			// position relative to right
			this.left = area.width - (this.width + this.right);
		}
	}

	/**
	 * Apply style rules
	 * @param {Layout} layout
	 * @param {ElementArea} area
	 */
	explicitLayout(layout, area) {
		layout.applyTo(this);
		if (this.absolute.isEmpty) { this.area.copy(this.absolute);	}
		if (is.value(area)) { this.absolute.add(area); }
	}

	/**
	 * Calculate missing area values
	 * @param {ElementArea} area
	 */
	implicitLayout(area) { }

	/**
	 * @param {Layout} layout
	 * @param {function} [callback]
	 */
	render(layout, callback) { this.explicitLayout(layout, this.area); }

	/**
	 * Top offset in pixels
	 * @returns {Number}
	 */
	get topPixels() { return Layout.inchesToPixels(this.top); }

	/**
	 * Left offset pixels
	 * @returns {Number}
	 */
	get leftPixels() { return Layout.inchesToPixels(this.left); }

	/**
	 * Width in pixels
	 * @returns {Number}
	 */
	get widthPixels() { return Layout.inchesToPixels(this.width); }

	/**
	 * Height in pixels
	 * @returns {Number}
	 */
	get heightPixels() { return Layout.inchesToPixels(this.height); }

	/**
	 * Set height in pixels
	 * @param {Number} p
	 */
	set heightPixels(p) { this.height = Layout.pixelsToInches(p); }

	/**
	 * Calculate left offset to center within width
	 * @param {Number} [width]
	 */
	center(width) {
		if (isNaN(this.width)) {
			this.alignContent = Layout.Align.Center;
		} else {
			this.left = (width - this.width) / 2;
		}
	}
}

module.exports = PDFElement;<commit_msg>checkpoint<commit_after>'use strict';

const TI = require('../../');
const is = TI.is;
const ElementArea = TI.PDF.Element.Area;
const Layout = TI.PDF.Layout;

/**
 * Base class for printable elements
 */
class PDFElement {
	/**
	 * All dimensions in inches
	 * @param {String} [style] pdf-style.json rule
	 */
	constructor(style) {
		/**
		 * Right-side distance from containing area
		 * @type {Number}
		 */
		this.right = NaN;
		/**
		 * Bottom distance from containing area
		 * @type {Number}
		 */
		this.bottom = NaN;
		/** @type {ElementArea} */
		this.area = new ElementArea();
		/**
		 * Element dimensions relative to PDF page
		 * @type {ElementArea}
		 */
		this.absolute = new ElementArea();
		/** @type {Number} */
		this.minWidth = NaN;
		/** @type {Number} */
		this.minHeight = NaN;
		/** @type {String} */
		this.alignContent = Layout.Align.Left;
		/** @type {String} */
		this.verticalAlign = Layout.Align.Top;
		/**
		 * How to scale element
		 * @type {String}
		 */
		this.scaleTo = Layout.Scale.None;

		/**
		 * Style name to match in pdf-style.json rules
		 * @type {String}
		 */
		this.style = style;

		/**
		 * RGB color
		 * @type {Number[]}
		 */
		this._color = [0, 0, 0, 1];

		/**
		 * RGBA color
		 * @type {Number[]}
		 */
		this.backgroundColor = [];

		/** @type {Number} */
		this.zIndex = 0;
	}

	/** @param {Number} n */
	set width(n) { this.area.width = n; }
	/** @return {Number} */
	get width() { return this.area.width; }

	/** @param {Number} n */
	set height(n) { this.area.height = n; }
	/** @return {Number} */
	get height() { return this.area.height; }

	/** @param {Number} n */
	set left(n) { this.area.left = n; }
	/** @return {Number} */
	get left() { return this.area.left; }

	/** @param {Number} n */
	set top(n) { this.area.top = n; }
	/** @return {Number} */
	get top() { return this.area.top; }

	/**
	 * @returns {Number[]}
	 */
	get color() {
		if (is.array(this._color)) {
			return (this._color.length > 3) ? this._color.slice(0, 3) : this._color;
		} else {
			return [0, 0, 0];
		}
	}

	/**
	 * @param {Number[]} c
	 */
	set color(c) { this._color = c; }

	/**
	 *
	 * @returns {Number}
	 */
	get opacity() {
		return (is.array(this._color) && this._color.length > 3) ? this._color[3] : 1;
	}

	/**
	 * @param {Number} a
	 */
	set opacity(a) {
		if (is.array(this._color)) {
			this._color[3] = a;
		} else {
			this._color = [0, 0, 0, a];
		}
	}

	/**
	 * Whether full layout is defined for PDF rendering
	 * @returns {Boolean}
	 */
	get laidOut() { return !this.area.isEmpty; }

	/**
	 * Set width and height for elements with anchored opposite edges
	 * Previously set width and height will be replaced
	 * @param {ElementArea} maxArea
	 */
	scale(maxArea) {
		if (!isNaN(this.left) && !isNaN(this.right)) {
			// adjust width to fit left and right parameters
			this.width = maxArea.width - (this.left + this.right);
		}

		if (!isNaN(this.top) && !isNaN(this.bottom)) {
			// adjust height to fit top and bottom parameters
			this.height = maxArea.height - (this.top + this.bottom);
		}
	}

	/**
	 * Position elements that are anchored to only one edge
	 * @param {ElementArea} area
	 */
	positionWithin(area) {
		if (!isNaN(this.bottom) && !isNaN(this.height) && isNaN(this.top)) {
			// position relative to bottom
			this.top = area.height - (this.height + this.bottom);
		}

		if (!isNaN(this.right) && !isNaN(this.width) && isNaN(this.left)) {
			// position relative to right
			this.left = area.width - (this.width + this.right);
		}
	}

	/**
	 * Apply style rules
	 * @param {PDFLayout} layout
	 * @param {ElementArea} area
	 */
	explicitLayout(layout, area) {
		layout.applyTo(this);
		if (this.absolute.isEmpty) { this.area.copy(this.absolute);	}
		if (is.value(area)) { this.absolute.add(area); }
	}

	/**
	 * Calculate missing area values
	 * @param {ElementArea} area
	 */
	implicitLayout(area) { }

	/**
	 * @param {Layout} layout
	 * @param {function} [callback]
	 */
	render(layout, callback) { this.explicitLayout(layout, this.area); }

	/**
	 * Top offset in pixels
	 * @returns {Number}
	 */
	get topPixels() { return Layout.inchesToPixels(this.top); }

	/**
	 * Left offset pixels
	 * @returns {Number}
	 */
	get leftPixels() { return Layout.inchesToPixels(this.left); }

	/**
	 * Width in pixels
	 * @returns {Number}
	 */
	get widthPixels() { return Layout.inchesToPixels(this.width); }

	/**
	 * Height in pixels
	 * @returns {Number}
	 */
	get heightPixels() { return Layout.inchesToPixels(this.height); }

	/**
	 * Set height in pixels
	 * @param {Number} p
	 */
	set heightPixels(p) { this.height = Layout.pixelsToInches(p); }

	/**
	 * Calculate left offset to center within width
	 * @param {Number} [width]
	 */
	center(width) {
		if (isNaN(this.width)) {
			this.alignContent = Layout.Align.Center;
		} else {
			this.left = (width - this.width) / 2;
		}
	}
}

module.exports = PDFElement;<|endoftext|>


<commit_before>//@auth
//@req(url, name)

import com.hivext.api.core.utils.Transport;

var url = getParam('url'), description = "start-stop-scheduler", resp, tasks;

if (url) {
    //reading script from URL
    var body = new Transport().get(url);

    //delete the script if it exists already
    jelastic.dev.scripting.DeleteScript(name);

    //create a new script
    resp = jelastic.dev.scripting.CreateScript(name, 'js', body);
    if (resp.result != 0) return resp;
}

resp = jelastic.utils.scheduler.GetTasks();
if (resp.result != 0) return resp;

tasks = resp.objects;
for (var i = 0, l = tasks.length; i < l; i++)
    if (tasks[i].script == name) jelastic.utils.scheduler.RemoveTask(tasks[i].id);

var startCron = getParam('start'),
    stopCron = getParam('stop');

if (startCron) {
    resp = addTask(startCron, 'start')
    if (resp.result != 0) return resp;
}

return stopCron ? addTask(stopCron, 'stop') : resp

function addTask (action, taskName) {
    //trim string
    var arr = action.replace(/\s+/g, ' ').replace(/^\s+|\s+$/gm,'').split(' ');
    //should be 5 elements
    if (arr.length != 5) return {result: 99, message:'wrong cron format [' + action + ']', type: 'error'}
    //replacing * at "Day of week" to make it compatible with quartz format
    if (arr[4] == '*') arr[4] = '?';
    //adding seconds - always 0
    arr.unshift('0');
    action = arr.join(' ');

    var params = toJSON({action: taskName, envName: '${env.envName}'});
    return jelastic.utils.scheduler.AddTask(appid, session, name, "cron:" + action, description, params);
}
<commit_msg>Update create-service.js<commit_after>//@auth
//@req(name)

import com.hivext.api.core.utils.Transport;

var url = getParam('url'), description = "start-stop-scheduler", resp, tasks;

if (url) {
    //reading script from URL
    var body = new Transport().get(url);

    //delete the script if it exists already
    jelastic.dev.scripting.DeleteScript(name);

    //create a new script
    resp = jelastic.dev.scripting.CreateScript(name, 'js', body);
    if (resp.result != 0) return resp;
}

resp = jelastic.utils.scheduler.GetTasks();
if (resp.result != 0) return resp;

tasks = resp.objects;
for (var i = 0, l = tasks.length; i < l; i++)
    if (tasks[i].script == name) jelastic.utils.scheduler.RemoveTask(tasks[i].id);

var startCron = getParam('start'),
    stopCron = getParam('stop');

if (startCron) {
    resp = addTask(startCron, 'start')
    if (resp.result != 0) return resp;
}

return stopCron ? addTask(stopCron, 'stop') : resp

function addTask (action, taskName) {
    //trim string
    var arr = action.replace(/\s+/g, ' ').replace(/^\s+|\s+$/gm,'').split(' ');
    //should be 5 elements
    if (arr.length != 5) return {result: 99, message:'wrong cron format [' + action + ']', type: 'error'}
    //replacing * at "Day of week" to make it compatible with quartz format
    if (arr[4] == '*') arr[4] = '?';
    //adding seconds - always 0
    arr.unshift('0');
    action = arr.join(' ');

    var params = toJSON({action: taskName, envName: '${env.envName}'});
    return jelastic.utils.scheduler.AddTask(appid, session, name, "cron:" + action, description, params);
}
<|endoftext|>


<commit_before>(function (document) {
  var handler = function (e) {
    var userInfo = {
        name: e.target.nodeName,
        url: e.target.src
    };
    safari.self.tab.setContextMenuEventUserInfo(event, userInfo)
  };
  window.addEventListener('contextmenu', handler, false);
})(window);
<commit_msg>Fix wrong indents.<commit_after>(function (document) {
  var handler = function (e) {
    var userInfo = {
      name: e.target.nodeName,
      url: e.target.src
    };
    safari.self.tab.setContextMenuEventUserInfo(event, userInfo)
  };
  window.addEventListener('contextmenu', handler, false);
})(window);
<|endoftext|>


<commit_before>process.env.NODE_ENV = 'production';

const AWS = require('aws-sdk');
const path = require('path');
const fs = require('fs');
const mime = require('mime');

const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
const queriesBucket = process.env.QUERIES_S3_BUCKET;
const persist = process.env.PERSIST_QUERIES === 'true';
const uploadParams = {
  Bucket: queriesBucket,
  Key: '',
  Body: '',
  ACL: 'public-read',
  // Sets Cache-Control header and in Metadata
  CacheControl: 'public, max-age=31536000',
};
const baseDir = './build/queries';

if (!persist) {
  console.error('Persist queries turned off. Exiting.');
  process.exit(0);
}

if (!queriesBucket) {
  console.error('QUERIES_S3_BUCKET is empty. Exiting.');
  process.exit(1);
}

fs.readdir(baseDir, (err, files) => {
  if (!files || files.length < 1) return;

  files.forEach((file) => {
    const fileStream = fs.createReadStream(path.join(baseDir, file));
    fileStream.on('error', (err) => {
      console.log('File Error', err);
    });
    uploadParams.Body = fileStream;
    uploadParams.Key = `queries/${file}`;

    // Sets Content-Type header and in Metadata
    const type = mime.getType(file);
    if (type) {
      uploadParams.ContentType = type;
    }
    s3.upload(uploadParams, (err, data) => {
      if (err) {
        console.log('Error', err);
      }
      if (data) {
        console.log('Queries upload Success', data.Location);
      }
    });
  });
});
<commit_msg>cache bust pq<commit_after>process.env.NODE_ENV = 'production';

const AWS = require('aws-sdk');
const path = require('path');
const fs = require('fs');
const mime = require('mime');

const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
const queriesBucket = process.env.QUERIES_S3_BUCKET;
const persist = process.env.PERSIST_QUERIES === 'true';
const uploadParams = {
  Bucket: queriesBucket,
  Key: '',
  Body: '',
  ACL: 'public-read',
  // Sets Cache-Control header and in Metadata
  CacheControl: 'public, max-age=31536000',
};
const baseDir = './build/queries';

if (!persist) {
  console.error('Persist queries turned off. Exiting.');
  process.exit(0);
}

if (!queriesBucket) {
  console.error('QUERIES_S3_BUCKET is empty. Exiting.');
  process.exit(1);
}

fs.readdir(baseDir, (err, files) => {
  if (!files || files.length < 1) return;

  files.forEach((file) => {
    const fileStream = fs.createReadStream(path.join(baseDir, file));
    fileStream.on('error', (err) => {
      console.log('File Error', err);
    });
    uploadParams.Body = fileStream;
    // FIXME: remove STATIC_QUERY_SUFFIX when the following is resolved:
    // https://github.com/facebook/relay/pull/2641
    uploadParams.Key = `queries/${file}${process.env.STATIC_QUERY_SUFFIX}`;

    // Sets Content-Type header and in Metadata
    const type = mime.getType(file);
    if (type) {
      uploadParams.ContentType = type;
    }
    s3.upload(uploadParams, (err, data) => {
      if (err) {
        console.log('Error', err);
      }
      if (data) {
        console.log('Queries upload Success', data.Location);
      }
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Copyright 2016 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {CSS} from '../../../build/amp-accordion-0.1.css';
import {Layout} from '../../../src/layout';
import {dev, user} from '../../../src/log';
import {removeFragment} from '../../../src/url';
import {map} from '../../../src/utils/object';

class AmpAccordion extends AMP.BaseElement {

  /** @param {!AmpElement} element */
  constructor(element) {
    super(element);

    /** @private {?NodeList} */
    this.sections_ = null;

    /** @private {?string} */
    this.sessionId_ = null;

    /** @private {?Object} */
    this.currentState_ = null;

    /** @private {boolean} */
    this.sessionOptOut_ = false;
  }

  /** @override */
  isLayoutSupported(layout) {
    return layout == Layout.CONTAINER;
  }

  /** @override */
  buildCallback() {
    this.sections_ = this.getRealChildren();

    this.sessionOptOut_ = this.element.hasAttribute('disable-session-states');

    this.element.setAttribute('role', 'tablist');

    // sessionStorage key: special created id for this element, this.sessionId_.
    // sessionStorage value: string that can convert to this.currentState_ obj.
    this.sessionId_ = this.getSessionStorageKey_();
    this.currentState_ = this.getSessionState_();

    this.sections_.forEach((section, index) => {
      user().assert(
          section.tagName.toLowerCase() == 'section',
          'Sections should be enclosed in a <section> tag, ' +
          'See https://github.com/ampproject/amphtml/blob/master/extensions/' +
          'amp-accordion/amp-accordion.md. Found in: %s', this.element);
      const sectionComponents_ = section.children;
      user().assert(
          sectionComponents_.length == 2,
          'Each section must have exactly two children. ' +
          'See https://github.com/ampproject/amphtml/blob/master/extensions/' +
          'amp-accordion/amp-accordion.md. Found in: %s', this.element);
      const header = sectionComponents_[0];
      const content = sectionComponents_[1];
      header.classList.add('i-amphtml-accordion-header');
      header.setAttribute('role', 'tab');
      content.classList.add('i-amphtml-accordion-content');
      content.setAttribute('role', 'tabpanel');
      let contentId = content.getAttribute('id');
      if (!contentId) {
        contentId = this.element.id + '_AMP_content_' + index;
        content.setAttribute('id', contentId);
      }
      if (this.currentState_[contentId]) {
        section.setAttribute('expanded', '');
      } else if (this.currentState_[contentId] == false) {
        section.removeAttribute('expanded');
      }
      header.setAttribute('aria-controls', contentId);
      header.setAttribute('aria-expanded',
          section.hasAttribute('expanded').toString());
      header.addEventListener('click', this.onHeaderClick_.bind(this));
    });
  }

  /**
   * Generate a sessionStorage Key based on amp-accordion element id.
   * @return {?string}
   * @private
   */
  getSessionStorageKey_() {
    const id_ = this.element.id || this.element.getResourceId();
    const url = removeFragment(this.win.location.href);
    return `amp-${id_}-${url}`;
  }

  /**
   * Get previous state from sessionStorage.
   * @return {!Object}
   * @private
   */
  getSessionState_() {
    if (this.sessionOptOut_) {
      return map();
    }
    try {
      const sessionStr =
          this.win./*OK*/sessionStorage.getItem(
          dev().assertString(this.sessionId_));
      return sessionStr
          ? /** @type {!Object} */ (JSON.parse(dev().assertString(sessionStr)))
          : map();
    } catch (e) {
      dev().fine('AMP-ACCORDION', e.message, e.stack);
      return map();
    }
  }

  /**
   * Set current state to sessionStorage.
   * @private
   */
  setSessionState_() {
    if (this.sessionOptOut_) {
      return;
    }
    const sessionStr = JSON.stringify(this.currentState_);
    try {
      this.win./*OK*/sessionStorage.setItem(
          dev().assertString(this.sessionId_), sessionStr);
    } catch (e) {
      dev().fine('AMP-ACCORDION', e.message, e.stack);
    }
  }

  /**
   * Handles accordion headers clicks to expand/collapse its content.
   * @param {!Event} event Click event.
   * @private
   */
  onHeaderClick_(event) {
    event.preventDefault();
    /** @const {!Element} */
    const section = event.currentTarget.parentNode;
    const sectionComponents_ = section.children;
    const header = sectionComponents_[0];
    const content = sectionComponents_[1];
    const contentId = content.getAttribute('id');
    const isSectionClosedAfterClick = section.hasAttribute('expanded');
    this.mutateElement(() => {
      if (section.hasAttribute('expanded')) {
        section.removeAttribute('expanded');
        header.setAttribute('aria-expanded', 'false');
      } else {
        section.setAttribute('expanded', '');
        header.setAttribute('aria-expanded', 'true');
      }
    }, section);
    this.currentState_[contentId] = !isSectionClosedAfterClick;
    this.setSessionState_();
  }
}

AMP.registerElement('amp-accordion', AmpAccordion, CSS);
<commit_msg>amp-accordion peek-a-boo bug (#9221)<commit_after>/**
 * Copyright 2016 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {CSS} from '../../../build/amp-accordion-0.1.css';
import {Layout} from '../../../src/layout';
import {dev, user} from '../../../src/log';
import {removeFragment} from '../../../src/url';
import {map} from '../../../src/utils/object';

class AmpAccordion extends AMP.BaseElement {

  /** @param {!AmpElement} element */
  constructor(element) {
    super(element);

    /** @private {?NodeList} */
    this.sections_ = null;

    /** @private {?string} */
    this.sessionId_ = null;

    /** @private {?Object} */
    this.currentState_ = null;

    /** @private {boolean} */
    this.sessionOptOut_ = false;
  }

  /** @override */
  isLayoutSupported(layout) {
    return layout == Layout.CONTAINER;
  }

  /** @override */
  buildCallback() {
    this.sections_ = this.getRealChildren();

    this.sessionOptOut_ = this.element.hasAttribute('disable-session-states');

    this.element.setAttribute('role', 'tablist');

    // sessionStorage key: special created id for this element, this.sessionId_.
    // sessionStorage value: string that can convert to this.currentState_ obj.
    this.sessionId_ = this.getSessionStorageKey_();
    this.currentState_ = this.getSessionState_();

    this.sections_.forEach((section, index) => {
      user().assert(
          section.tagName.toLowerCase() == 'section',
          'Sections should be enclosed in a <section> tag, ' +
          'See https://github.com/ampproject/amphtml/blob/master/extensions/' +
          'amp-accordion/amp-accordion.md. Found in: %s', this.element);
      const sectionComponents_ = section.children;
      user().assert(
          sectionComponents_.length == 2,
          'Each section must have exactly two children. ' +
          'See https://github.com/ampproject/amphtml/blob/master/extensions/' +
          'amp-accordion/amp-accordion.md. Found in: %s', this.element);

      this.mutateElement(() => {
        const header = sectionComponents_[0];
        const content = sectionComponents_[1];
        header.classList.add('i-amphtml-accordion-header');
        header.setAttribute('role', 'tab');
        content.classList.add('i-amphtml-accordion-content');
        content.setAttribute('role', 'tabpanel');
        let contentId = content.getAttribute('id');
        if (!contentId) {
          contentId = this.element.id + '_AMP_content_' + index;
          content.setAttribute('id', contentId);
        }
        if (this.currentState_[contentId]) {
          section.setAttribute('expanded', '');
        } else if (this.currentState_[contentId] == false) {
          section.removeAttribute('expanded');
        }
        header.setAttribute('aria-controls', contentId);
        header.setAttribute('aria-expanded',
            section.hasAttribute('expanded').toString());
        header.addEventListener('click', this.onHeaderClick_.bind(this));
      });
    });
  }

  /**
   * Generate a sessionStorage Key based on amp-accordion element id.
   * @return {?string}
   * @private
   */
  getSessionStorageKey_() {
    const id_ = this.element.id || this.element.getResourceId();
    const url = removeFragment(this.win.location.href);
    return `amp-${id_}-${url}`;
  }

  /**
   * Get previous state from sessionStorage.
   * @return {!Object}
   * @private
   */
  getSessionState_() {
    if (this.sessionOptOut_) {
      return map();
    }
    try {
      const sessionStr =
          this.win./*OK*/sessionStorage.getItem(
          dev().assertString(this.sessionId_));
      return sessionStr
          ? /** @type {!Object} */ (JSON.parse(dev().assertString(sessionStr)))
          : map();
    } catch (e) {
      dev().fine('AMP-ACCORDION', e.message, e.stack);
      return map();
    }
  }

  /**
   * Set current state to sessionStorage.
   * @private
   */
  setSessionState_() {
    if (this.sessionOptOut_) {
      return;
    }
    const sessionStr = JSON.stringify(this.currentState_);
    try {
      this.win./*OK*/sessionStorage.setItem(
          dev().assertString(this.sessionId_), sessionStr);
    } catch (e) {
      dev().fine('AMP-ACCORDION', e.message, e.stack);
    }
  }

  /**
   * Handles accordion headers clicks to expand/collapse its content.
   * @param {!Event} event Click event.
   * @private
   */
  onHeaderClick_(event) {
    event.preventDefault();
    /** @const {!Element} */
    const section = event.currentTarget.parentNode;
    const sectionComponents_ = section.children;
    const header = sectionComponents_[0];
    const content = sectionComponents_[1];
    const contentId = content.getAttribute('id');
    const isSectionClosedAfterClick = section.hasAttribute('expanded');
    this.mutateElement(() => {
      if (section.hasAttribute('expanded')) {
        section.removeAttribute('expanded');
        header.setAttribute('aria-expanded', 'false');
      } else {
        section.setAttribute('expanded', '');
        header.setAttribute('aria-expanded', 'true');
      }
    }, section);
    this.currentState_[contentId] = !isSectionClosedAfterClick;
    this.setSessionState_();
  }
}

AMP.registerElement('amp-accordion', AmpAccordion, CSS);
<|endoftext|>


<commit_before>/**
 * Copyright 2015 The AMP HTML Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Shows a Pinterest widget.
 * Examples:
 *
 *
 * <amp-pinterest height=20 width=40
 *   data-do="buttonPin"
 *   data-url="http://www.flickr.com/photos/kentbrew/6851755809/"
 *   data-media="http://farm8.staticflickr.com/7027/6851755809_df5b2051c9_z.jpg"
 *   data-description="Next stop: Pinterest">
 * </amp-pinterest>
 *
 * <amp-pinterest width=245 height=330
 *   data-do="embedPin"
 *   data-url="https://www.pinterest.com/pin/99360735500167749/">
 * </amp-pinterest>
 *
 *
 */

import {CSS} from '../../../build/amp-pinterest-0.1.css';
import {isLayoutSizeDefined} from '../../../src/layout';
import {user} from '../../../src/log';

import {FollowButton} from './follow-button';
import {PinItButton} from './pinit-button';
import {PinWidget} from './pin-widget';

/**
 * AMP Pinterest
 * @attr data-do
 *    - buttonPin: Pin It button
 *    - buttonFollow: User follow button
 */
class AmpPinterest extends AMP.BaseElement {

  /** @override */
  preconnectCallback(onLayout) {
    // preconnect to widget APIpinMedia
    this.preconnect.url('https://widgets.pinterest.com', onLayout);
  }

  /** @override */
  isLayoutSupported(layout) {
    return isLayoutSizeDefined(layout);
  }

  /** @override */
  layoutCallback() {
    const selector = user().assert(this.element.getAttribute('data-do'),
      'The data-do attribute is required for <amp-pinterest> %s',
      this.element);

    return this.render(selector).then(node => {
      return this.element.appendChild(node);
    });
  }

  render(selector) {
    switch (selector) {
      case 'embedPin':
        return new PinWidget(this.element).render();
      case 'buttonPin':
        return new PinItButton(this.element).render();
      case 'buttonFollow':
        return new FollowButton(this.element).render();
    }
    return Promise.resolve('Invalid selector: ', selector);
  }

};

AMP.registerElement('amp-pinterest', AmpPinterest, CSS);
<commit_msg>Fix Pinterest reporting when unknown selector (#7447)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Shows a Pinterest widget.
 * Examples:
 *
 *
 * <amp-pinterest height=20 width=40
 *   data-do="buttonPin"
 *   data-url="http://www.flickr.com/photos/kentbrew/6851755809/"
 *   data-media="http://farm8.staticflickr.com/7027/6851755809_df5b2051c9_z.jpg"
 *   data-description="Next stop: Pinterest">
 * </amp-pinterest>
 *
 * <amp-pinterest width=245 height=330
 *   data-do="embedPin"
 *   data-url="https://www.pinterest.com/pin/99360735500167749/">
 * </amp-pinterest>
 *
 *
 */

import {CSS} from '../../../build/amp-pinterest-0.1.css';
import {isLayoutSizeDefined} from '../../../src/layout';
import {user} from '../../../src/log';

import {FollowButton} from './follow-button';
import {PinItButton} from './pinit-button';
import {PinWidget} from './pin-widget';

/**
 * AMP Pinterest
 * @attr data-do
 *    - buttonPin: Pin It button
 *    - buttonFollow: User follow button
 */
class AmpPinterest extends AMP.BaseElement {

  /** @override */
  preconnectCallback(onLayout) {
    // preconnect to widget APIpinMedia
    this.preconnect.url('https://widgets.pinterest.com', onLayout);
  }

  /** @override */
  isLayoutSupported(layout) {
    return isLayoutSizeDefined(layout);
  }

  /** @override */
  layoutCallback() {
    const selector = user().assert(this.element.getAttribute('data-do'),
      'The data-do attribute is required for <amp-pinterest> %s',
      this.element);

    return this.render(selector).then(node => {
      return this.element.appendChild(node);
    });
  }

  render(selector) {
    switch (selector) {
      case 'embedPin':
        return new PinWidget(this.element).render();
      case 'buttonPin':
        return new PinItButton(this.element).render();
      case 'buttonFollow':
        return new FollowButton(this.element).render();
    }
    return Promise.reject(user().createError('Invalid selector: ' + selector));
  }

};

AMP.registerElement('amp-pinterest', AmpPinterest, CSS);
<|endoftext|>