<commit_before>'use strict';
/* Parser.js -- transforms keyboard input into tokens.
 + key_handler: KeyboardEvent -> ModifiedKeyboardEvent
 + update: ModifiedKeyboardEvent -> ParserStatus

 NOTE: These 2 functions communicate implicitly via mutable internal state (kh).
 I know, I know.

 Currently key_handler calls update directly, but if update becomes a bottleneck
 it can instead be enqueued via requestAnimationFrame. */
const Parser=(logging=0)=>{
  const chord={/* ChordName:{Action,KeyCode,[Mods]} */
    'C-[':{type:'escape',code:'BracketLeft',mods:[2]},
    'C-g':{type:'escape',code:'KeyG',mods:[2]},
    'C-d':{type:'motion',code:'KeyD',mods:[2]},
    'C-u':{type:'motion',code:'KeyU',mods:[2]},
    'C-f':{type:'motion',code:'KeyF',mods:[2]},
    'C-b':{type:'motion',code:'KeyB',mods:[2]},
    'C-h':{type:'motion',code:'KeyH',mods:[2]},
    'C-j':{type:'motion',code:'KeyJ',mods:[2]},
    'C-k':{type:'motion',code:'KeyK',mods:[2]},
    'C-l':{type:'motion',code:'KeyL',mods:[2]},
    'C-v':{type:'visual',code:'KeyV',mods:[2]}
  };

  const seq=(()=>{/* {String:{Action,ReverseName,MinimumMsBetween?}} */
    let t={
      fd:{type:'escape',dt:200},
      cc:{type:'phrase'},
      dd:{type:'phrase'},
      yy:{type:'phrase'},
      gg:{type:'phrase'},
      '``':{type:'phrase'},
      cs:{type:'csurround'},
      ds:{type:'dsurround'},
      ys:{type:'ysurround'}
    }; for(let x in t){t[x].rn=[...x].reverse().join('');} return t;
  })();

  const range=(a,b,c=1)=>{let r=[];while(a<b){r.push(a);a+=c;}return r;};
  const atom=(()=>{/* {Char:[Type]} */
    let xs={
      ascii:'',
      bracket:'[{()}]',
      edit:'oOpPrxX~',
      insert:'aAiI',
      leader:' ',
      modifier:'ai',
      motion:'hjkl',
      mult_0:'123456789',
      mult_N:'0123456789',
      repeat:'.',
      search:'/?',
      seek:'fFtT',
      tag:' 0123456789=:-',
      tag_end:'/>',
      tag_start:'t',
      text_object:'0^$%{}()[]<>`"\'bBeEpwWG',
      undo:'u',
      verb:'cdy`',
      visual:'vV'
    };
    [['ascii',32,127,9],['tag',65,90],['tag',97,122]]
      .forEach(([o,x,y,...others])=>{xs[o]+=String.fromCharCode(...range(x,y+1).concat(others));});
    xs.ascii+=String.fromCharCode(9);
    let t={}; for(let x in xs){[...xs[x]].forEach(y=>t[y]?t[y].push(x):t[y]=[x]);}
    t.Enter=['enter']; t.Escape=['escape']; t.Tab=['tab'];
    t.Delete=t.Backspace=['edit'];
    ['Right','Left','Up','Down'].map(x=>t['Arrow'+x]=['arrow']);
    return t;
  })();

  /* Leader Tree */
  const lt=()=>({tab:leaf, ascii:leaf});/* TODO */

  const leaf=1, branch=2, nomatch=3;
  const st=(n=0)=>{/* State Tree */
    const bt=({bracket:leaf, tag_start:{get tag(){return this;}, tag_end:leaf}}),
          re=({enter:leaf, get ascii(){return this;}});
    return((x)=>{
      if(n===1){
        delete x.mult_0;
        Object.defineProperty(x,'mult_N',{get:function(){return this;}});
      } return x;
    })({
      get mult_0(){return st(1);},
      get leader(){return lt();},
      tab:leaf,
      edit:leaf,
      arrow:leaf,
      escape:leaf,
      motion:leaf,
      phrase:leaf,
      repeat:leaf,
      text_object:leaf,
      undo:leaf,
      search:re,
      seek:{ascii:leaf},
      visual:leaf,
      verb:{
        motion:leaf,
        phrase:leaf,
        text_object:leaf,
        seek:{ascii:leaf},
        modifier:{seek:{ascii:leaf}, text_object:leaf},
        csurround:{bracket:bt, tag_start:bt},
        dsurround:{bracket:leaf, tag_start:leaf},
        ysurround:{
          bracket:bt,
          modifier:{motion:bt, seek:{ascii:bt}, text_object:bt},
          motion:bt,
          seek:{ascii:bt},
          text_object:bt}}});
  };

  let stt=st(), vals={keys:[],mods:[],part:[]};
  const get_stt=()=>{return stt;};
  const reset_stt=()=>{stt=st(); vals={keys:[],mods:[],part:[]};};

  const maybe_chord=(n)=>{
    const m=n[0].mods; if(!m){return null;}
    const kc=n[0].chord; if(kc.length<2){return null;}
    for(let x in chord){
      const c=chord[x], keyp=kc.includes(c.code), modp=c.mods.some(y=>y===m);
      if(modp && keyp){return ({type:c.type, take:kc.length});}
    } return null;
  };

  const maybe_seq=(n)=>{
    const ns=n.map(x=>x.key).join('');
    if(2>ns.length){return null;}
    const dts=n.map(x=>x.ts), snds=dts.slice(1),
          deltas=(s)=>!s.dt || dts.slice(0,s.rn.length-1).map((x,i)=>x-snds[i]).every(x=>s.dt>x);
    for(let x in seq){
      let s=seq[x];
      if(ns.startsWith(s.rn) && deltas(s)){return ({type:s.type, take:x.length});}
    } return null;
  };

  const maybe_atom=(n)=>{
    const a=atom[n[0].key];
    console.log(a);
    if(!a){return null;}
    const m=n[0].chord, ns=Object.getOwnPropertyNames(stt);
    for(let i in a){
      if((0===m || 8===m) && ns.includes(a[i])){return ({type:a[i], take:0});}
    } return null;
  };

  /* Input => (leaf|branch|nomatch) */
  const climb_tree=(x)=>{
    let z=stt[x]; if(z){
      vals.part.push(x);
      if(leaf===z){return leaf;}
      stt=z; return branch;
    } return nomatch;
  };

  let inq=[];/* input queue */
  const update=(input)=>{
    inq.unshift(input);/* prepend */
    const fns=[maybe_chord,maybe_seq,maybe_atom],
          R=(a,b)=>{let r={}; if(b&2){r=vals;} if(b&1){reset_stt();} r.status=a; return r;};
    let t=null; for(let i in fns){
      if((t=fns[i](inq))){
        inq=inq.slice(t.take);
        if('escape'===t.type){return R('quit',1);}
        vals.keys.push(input.key);
        vals.mods.push(input.mods);
        t=climb_tree(t.type);
        if(nomatch===t){return R('error',1);}
        if(leaf===t){return R('done',3);}
        if(branch===t){return R('continue',2);}
      }
    }
    if((t=atom[input.key]) && t.includes('ascii')){return R('error',1);}
    if(input.mods){return R('ignore',0);}
    return R('error',1);
  };

  const KC=new Set();
  const key_handler=(ev,up)=>{/* First encode/enqueue the input, then schedule an update. */
    KC[up?'delete':'add'](ev.code); if(up){return null;}
    const evt={key:ev.key, code:ev.code, ts:ev.timeStamp|0,
               mods:['altKey','ctrlKey','metaKey','shiftKey'].reduce((a,b,i)=>a|((ev[b]|0)<<i),0),
               chord:Array.from(KC)},
          ad={'KeyI':[10,5],'KeyR':[2,4]}[evt[1]];/* allow default */
    if(ad && ad[navigator.platform==='MacIntel'|0]===evt.mods){return null;}
    ev.preventDefault();
    return update(evt);
  };
  return {key_handler};
};
<commit_msg>more reshaping<commit_after>'use strict';
/* Parser.js -- transforms keyboard input into tokens.
 + key_handler: KeyboardEvent -> ModifiedKeyboardEvent && ()
 + update: ModifiedKeyboardEvent -> ParserStatus

 key_handler ALSO updates the global variable KC, which holds all the currently-pressed keys.
 I know, I know.

 Currently key_handler calls update directly, but if update becomes a bottleneck
 it can instead be enqueued via requestAnimationFrame, or maybe even put into a Worker thread. */
const Parser=(logging=0)=>{
  const chord=[
    {code:'BracketLeft',type:'escape',mods:[2]},
    {code:'KeyG',type:'escape',mods:[2]},
    {code:'KeyD',type:'motion',mods:[2]},
    {code:'KeyU',type:'motion',mods:[2]},
    {code:'KeyF',type:'motion',mods:[2]},
    {code:'KeyB',type:'motion',mods:[2]},
    {code:'KeyH',type:'motion',mods:[2]},
    {code:'KeyJ',type:'motion',mods:[2]},
    {code:'KeyK',type:'motion',mods:[2]},
    {code:'KeyL',type:'motion',mods:[2]},
    {code:'KeyV',type:'visual',mods:[2]}
  ];

  const seq=[
    {rn:'df',type:'escape',dt:200},
    {rn:'cc',type:'phrase'},
    {rn:'dd',type:'phrase'},
    {rn:'yy',type:'phrase'},
    {rn:'gg',type:'phrase'},
    {rn:'``',type:'phrase'},
    {rn:'sc',type:'csurround'},
    {rn:'sd',type:'dsurround'},
    {rn:'sy',type:'ysurround'}
  ];

  const range=(a,b,c=1)=>{let r=[];while(a<b){r.push(a);a+=c;}return r;};
  const atom=(()=>{/* {Char:[Type]} */
    let xs={
      ascii:'',
      bracket:'[{()}]',
      edit:'oOpPrxX~',
      insert:'aAiI',
      leader:' ',
      modifier:'ai',
      motion:'hjkl',
      mult_0:'123456789',
      mult_N:'0123456789',
      repeat:'.',
      search:'/?',
      seek:'fFtT',
      tag:' 0123456789=:-',
      tag_end:'/>',
      tag_start:'t',
      text_object:'0^$%{}()[]<>`"\'bBeEpwWG',
      undo:'u',
      verb:'cdy`',
      visual:'vV'
    };
    [['ascii',32,127,9],['tag',65,90],['tag',97,122]]
      .forEach(([o,x,y,...others])=>{xs[o]+=String.fromCharCode(...range(x,y+1).concat(others));});
    xs.ascii+=String.fromCharCode(9);
    let t={}; for(let x in xs){[...xs[x]].forEach(y=>t[y]?t[y].push(x):t[y]=[x]);}
    t.Enter=['enter']; t.Escape=['escape']; t.Tab=['tab'];
    t.Delete=t.Backspace=['edit'];
    ['Right','Left','Up','Down'].map(x=>t['Arrow'+x]=['arrow']);
    return t;
  })();

  /* Leader Tree */
  const lt=()=>({tab:leaf, ascii:leaf});/* TODO */

  const leaf=1, branch=2, nomatch=3;
  const st=(n=0)=>{/* State Tree */
    const bt=({bracket:leaf, tag_start:{get tag(){return this;}, tag_end:leaf}}),
          re=({enter:leaf, get ascii(){return this;}});
    return((x)=>{
      if(n===1){
        delete x.mult_0;
        Object.defineProperty(x,'mult_N',{get:function(){return this;}});
      } return x;
    })({
      get mult_0(){return st(1);},
      get leader(){return lt();},
      tab:leaf,
      edit:leaf,
      arrow:leaf,
      escape:leaf,
      motion:leaf,
      phrase:leaf,
      repeat:leaf,
      text_object:leaf,
      undo:leaf,
      search:re,
      seek:{ascii:leaf},
      visual:leaf,
      verb:{
        motion:leaf,
        phrase:leaf,
        text_object:leaf,
        seek:{ascii:leaf},
        modifier:{seek:{ascii:leaf}, text_object:leaf},
        csurround:{bracket:bt, tag_start:bt},
        dsurround:{bracket:leaf, tag_start:leaf},
        ysurround:{
          bracket:bt,
          modifier:{motion:bt, seek:{ascii:bt}, text_object:bt},
          motion:bt,
          seek:{ascii:bt},
          text_object:bt}}});
  };

  /* Infernal State Variables  */
  let inq=[], stt=st(), vals={keys:[],mods:[],part:[]};
  const get_stt=()=>stt;
  const reset_stt=()=>{stt=st(); inq=[]; vals={keys:[],mods:[],part:[]};};

  const maybe_chord=(n)=>{
    const m=n[0].mods; if(!m){return null;}
    const kc=n[0].chord; if(kc.length<2){return null;}
    for(let {code:cc, mods:cm, type:ct} of chord){
      const keyp=kc.includes(cc),
            modp=cm.some(y=>y===m);
      if(modp && keyp){return ({type:ct, take:kc.length});}
    } return null;
  };

  const maybe_seq=(n)=>{
    const ns=n.map(x=>x.key).join('');
    if(2>ns.length){return null;}
    const dts=n.map(x=>x.ts), snds=dts.slice(1),
          deltas=(a,b)=>!a || dts.slice(0,b.length-1).map((x,i)=>x-snds[i]).every(x=>a>x);
    for(let {rn:sr, dt:sd, type:st} of seq){
      if(ns.startsWith(sr) && deltas(sd,sr)){return ({type:st, take:sr.length});}
    } return null;
  };

  const maybe_atom=(n)=>{
    const a=atom[n[0].key];
    if(!a){return null;}
    const m=n[0].mods, ns=Object.getOwnPropertyNames(stt);
    for(let i in a){
      if((0===m || 8===m) && ns.includes(a[i])){return ({type:a[i], take:0});}
    } return null;
  };

  /* Input => (leaf|branch|nomatch) */
  const climb_tree=(x)=>{
    let z=stt[x]; if(z){
      vals.part.push(x);
      if(leaf===z){return leaf;}
      stt=z; return branch;
    } return nomatch;
  };

  const update=(input)=>{
    inq.unshift(input);/* prepend */
    const fns=[maybe_chord,maybe_seq,maybe_atom],
          R=(a,b)=>{let r={}; if(b&2){r=vals;} if(b&1){reset_stt();} r.status=a; return r;};
    let t=null; for(let fn of fns){
      if((t=fn(inq))){
        inq=inq.slice(t.take);

        if('escape'===t.type){return R('quit',1);}

        vals.keys.push(input.key);
        vals.mods.push(input.mods);
        t=climb_tree(t.type);

        if(nomatch===t){return R('error',1);}
        if(leaf===t){return R('done',3);}
        if(branch===t){return R('continue',2);}
      }
    }
    if((t=atom[input.key]) && t.includes('ascii')){return R('error',1);}
    if(input.mods){return R('ignore',0);}
    return R('error',1);
  };

  const KC=new Set();
  const key_handler=(e,up)=>{/* First encode/enqueue the input, then schedule an update. */
    KC[up?'delete':'add'](e.code); if(up){return null;}
    const evt={key:e.key, code:e.code, ts:e.timeStamp|0,
               mods:['altKey','ctrlKey','metaKey','shiftKey'].reduce((a,b,i)=>a|((e[b]|0)<<i),0),
               chord:Array.from(KC)},
          ad={'KeyI':[10,5],'KeyR':[2,4]}[evt[1]];/* allow default */
    if(ad && ad[navigator.platform==='MacIntel'|0]===evt.mods){return null;}
    e.preventDefault();
    return update(evt);
  };
  return {key_handler};
};
<|endoftext|>


<commit_before>var MARGIN = 16;
var MARGIN_LARGE = 24;

var cpsCount = 0;
var startTime = new Date().getTime();
var taskId;

var statusTextView = new tabris.TextView({
  left: MARGIN, top: MARGIN_LARGE, right: MARGIN,
  text: 'Last update: <none>'
}).appendTo(tabris.ui.contentView);

var cpsTextView = new tabris.TextView({
  left: MARGIN, top: [statusTextView, MARGIN], right: MARGIN,
  text: 'Calls per second: <none>'
}).appendTo(tabris.ui.contentView);

var delayTextView = new tabris.TextView({
  left: MARGIN, baseline: '#delayTextInput',
  text: 'Delay (ms):'
}).appendTo(tabris.ui.contentView);

var delayTextInput = new tabris.TextInput({
  left: [delayTextView, MARGIN], top: [cpsTextView, MARGIN_LARGE],
  id: 'delayTextInput',
  text: '1000',
  message: 'Delay (ms)'
}).appendTo(tabris.ui.contentView);

var repeatCheckbox = new tabris.CheckBox({
  left: MARGIN, top: delayTextInput,
  text: 'Repeat'
}).appendTo(tabris.ui.contentView);

var startButton = new tabris.Button({
  left: ['50%', MARGIN / 4], top: [repeatCheckbox, MARGIN_LARGE], right: MARGIN,
  text: 'Start timer'
}).on('select', function() {
  var delay = parseInt(delayTextInput.text);
  if (repeatCheckbox.checked) {
    taskId = setInterval(updateStatusTextViews, delay);
  } else {
    taskId = setTimeout(function() {
      updateStatusTextViews();
      enableTimerStart(true);
    }, delay);
  }
  enableTimerStart(false);
}).appendTo(tabris.ui.contentView);

var cancelButton = new tabris.Button({
  left: MARGIN, top: [repeatCheckbox, MARGIN_LARGE], right: ['50%', MARGIN / 4],
  text: 'Cancel timer',
  enabled: false
}).on('select', function() {
  clearTimeout(taskId);
  enableTimerStart(true);
}).appendTo(tabris.ui.contentView);

function updateStatusTextViews() {
  cpsCount++;
  var curTime = new Date().getTime();
  var diff = curTime - startTime;
  if (diff >= 1000) {
    cpsTextView.text = 'Calls per second: ' + cpsCount;
    cpsCount = 0;
    startTime = curTime;
  }
  statusTextView.text = 'Last update: ' + new Date().getTime().toString();
}

function enableTimerStart(available) {
  startButton.enabled = available;
  cancelButton.enabled = !available;
}
<commit_msg>Fix windows layout exception of timer example<commit_after>var MARGIN = 16;
var MARGIN_LARGE = 24;

var cpsCount = 0;
var startTime = new Date().getTime();
var taskId;

var statusTextView = new tabris.TextView({
  left: MARGIN, top: MARGIN_LARGE, right: MARGIN,
  text: 'Last update: <none>'
}).appendTo(tabris.ui.contentView);

var cpsTextView = new tabris.TextView({
  left: MARGIN, top: [statusTextView, MARGIN], right: MARGIN,
  text: 'Calls per second: <none>'
}).appendTo(tabris.ui.contentView);

var delayTextView = new tabris.TextView({
  left: MARGIN, top: [cpsTextView, MARGIN_LARGE],
  text: 'Delay (ms):'
}).appendTo(tabris.ui.contentView);

var delayTextInput = new tabris.TextInput({
  left: [delayTextView, MARGIN], baseline: delayTextView,
  id: 'delayTextInput',
  text: '1000',
  message: 'Delay (ms)'
}).appendTo(tabris.ui.contentView);

var repeatCheckbox = new tabris.CheckBox({
  left: MARGIN, top: delayTextInput,
  text: 'Repeat'
}).appendTo(tabris.ui.contentView);

var startButton = new tabris.Button({
  left: ['50%', MARGIN / 4], top: [repeatCheckbox, MARGIN_LARGE], right: MARGIN,
  text: 'Start timer'
}).on('select', function() {
  var delay = parseInt(delayTextInput.text);
  if (repeatCheckbox.checked) {
    taskId = setInterval(updateStatusTextViews, delay);
  } else {
    taskId = setTimeout(function() {
      updateStatusTextViews();
      enableTimerStart(true);
    }, delay);
  }
  enableTimerStart(false);
}).appendTo(tabris.ui.contentView);

var cancelButton = new tabris.Button({
  left: MARGIN, top: [repeatCheckbox, MARGIN_LARGE], right: ['50%', MARGIN / 4],
  text: 'Cancel timer',
  enabled: false
}).on('select', function() {
  clearTimeout(taskId);
  enableTimerStart(true);
}).appendTo(tabris.ui.contentView);

function updateStatusTextViews() {
  cpsCount++;
  var curTime = new Date().getTime();
  var diff = curTime - startTime;
  if (diff >= 1000) {
    cpsTextView.text = 'Calls per second: ' + cpsCount;
    cpsCount = 0;
    startTime = curTime;
  }
  statusTextView.text = 'Last update: ' + new Date().getTime().toString();
}

function enableTimerStart(available) {
  startButton.enabled = available;
  cancelButton.enabled = !available;
}
<|endoftext|>


<commit_before>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* @namespace Phaser
*/
var Phaser = Phaser || { // jshint ignore:line

    /**
    * The Phaser version number.
    * @constant Phaser.VERSION
    * @type {string}
    */
    VERSION: '2.13.3',

    /**
    * An array of Phaser game instances.
    * @constant Phaser.GAMES
    * @type {array}
    */
    GAMES: [],

    /**
    * AUTO renderer - picks between WebGL or Canvas based on device.
    * @constant Phaser.AUTO
    * @type {integer}
    */
    AUTO: 0,

    /**
    * Canvas Renderer.
    * @constant Phaser.CANVAS
    * @type {integer}
    */
    CANVAS: 1,

    /**
    * WebGL Renderer.
    * @constant Phaser.WEBGL
    * @type {integer}
    */
    WEBGL: 2,

    /**
    * Headless renderer (not visual output)
    * @constant Phaser.HEADLESS
    * @type {integer}
    */
    HEADLESS: 3,

    /**
    * WebGL Renderer with MultiTexture support enabled.
    * @constant Phaser.WEBGL_MULTI
    * @type {integer}
    */
    WEBGL_MULTI: 4,

    /**
    * Direction constant.
    * @constant Phaser.NONE
    * @type {integer}
    */
    NONE: 0,

    /**
    * Direction constant.
    * @constant Phaser.LEFT
    * @type {integer}
    */
    LEFT: 1,

    /**
    * Direction constant.
    * @constant Phaser.RIGHT
    * @type {integer}
    */
    RIGHT: 2,

    /**
    * Direction constant.
    * @constant Phaser.UP
    * @type {integer}
    */
    UP: 3,

    /**
    * Direction constant.
    * @constant Phaser.DOWN
    * @type {integer}
    */
    DOWN: 4,

    /**
    * Game Object type.
    * @constant Phaser.SPRITE
    * @type {integer}
    */
    SPRITE: 0,

    /**
    * Game Object type.
    * @constant Phaser.BUTTON
    * @type {integer}
    */
    BUTTON: 1,

    /**
    * Game Object type.
    * @constant Phaser.IMAGE
    * @type {integer}
    */
    IMAGE: 2,

    /**
    * Game Object type.
    * @constant Phaser.GRAPHICS
    * @type {integer}
    */
    GRAPHICS: 3,

    /**
    * Game Object type.
    * @constant Phaser.TEXT
    * @type {integer}
    */
    TEXT: 4,

    /**
    * Game Object type.
    * @constant Phaser.TILESPRITE
    * @type {integer}
    */
    TILESPRITE: 5,

    /**
    * Game Object type.
    * @constant Phaser.BITMAPTEXT
    * @type {integer}
    */
    BITMAPTEXT: 6,

    /**
    * Game Object type.
    * @constant Phaser.GROUP
    * @type {integer}
    */
    GROUP: 7,

    /**
    * Game Object type.
    * @constant Phaser.RENDERTEXTURE
    * @type {integer}
    */
    RENDERTEXTURE: 8,

    /**
    * Game Object type.
    * @constant Phaser.TILEMAP
    * @type {integer}
    */
    TILEMAP: 9,

    /**
    * Game Object type.
    * @constant Phaser.TILEMAPLAYER
    * @type {integer}
    */
    TILEMAPLAYER: 10,

    /**
    * Game Object type.
    * @constant Phaser.EMITTER
    * @type {integer}
    */
    EMITTER: 11,

    /**
    * Game Object type.
    * @constant Phaser.POLYGON
    * @type {integer}
    */
    POLYGON: 12,

    /**
    * Game Object type.
    * @constant Phaser.BITMAPDATA
    * @type {integer}
    */
    BITMAPDATA: 13,

    /**
    * Game Object type.
    * @constant Phaser.CANVAS_FILTER
    * @type {integer}
    */
    CANVAS_FILTER: 14,

    /**
    * Game Object type.
    * @constant Phaser.WEBGL_FILTER
    * @type {integer}
    */
    WEBGL_FILTER: 15,

    /**
    * Game Object type.
    * @constant Phaser.ELLIPSE
    * @type {integer}
    */
    ELLIPSE: 16,

    /**
    * Game Object type.
    * @constant Phaser.SPRITEBATCH
    * @type {integer}
    */
    SPRITEBATCH: 17,

    /**
    * Game Object type.
    * @constant Phaser.RETROFONT
    * @type {integer}
    */
    RETROFONT: 18,

    /**
    * Game Object type.
    * @constant Phaser.POINTER
    * @type {integer}
    */
    POINTER: 19,

    /**
    * Game Object type.
    * @constant Phaser.ROPE
    * @type {integer}
    */
    ROPE: 20,

    /**
    * Game Object type.
    * @constant Phaser.CIRCLE
    * @type {integer}
    */
    CIRCLE: 21,

    /**
    * Game Object type.
    * @constant Phaser.RECTANGLE
    * @type {integer}
    */
    RECTANGLE: 22,

    /**
    * Game Object type.
    * @constant Phaser.LINE
    * @type {integer}
    */
    LINE: 23,

    /**
    * Game Object type.
    * @constant Phaser.MATRIX
    * @type {integer}
    */
    MATRIX: 24,

    /**
    * Game Object type.
    * @constant Phaser.POINT
    * @type {integer}
    */
    POINT: 25,

    /**
    * Game Object type.
    * @constant Phaser.ROUNDEDRECTANGLE
    * @type {integer}
    */
    ROUNDEDRECTANGLE: 26,

    /**
    * Game Object type.
    * @constant Phaser.CREATURE
    * @type {integer}
    */
    CREATURE: 27,

    /**
    * Game Object type.
    * @constant Phaser.VIDEO
    * @type {integer}
    */
    VIDEO: 28,

    /**
    * Game Object type.
    * @constant Phaser.PENDING_ATLAS
    * @type {integer}
    */
    PENDING_ATLAS: -1,

    /**
    * A horizontal orientation
    * @constant Phaser.HORIZONTAL
    * @type {integer}
    */
    HORIZONTAL: 0,

    /**
    * A vertical orientation
    * @constant Phaser.VERTICAL
    * @type {integer}
    */
    VERTICAL: 1,

    /**
    * A landscape orientation
    * @constant Phaser.LANDSCAPE
    * @type {integer}
    */
    LANDSCAPE: 0,

    /**
    * A portrait orientation
    * @constant Phaser.PORTRAIT
    * @type {integer}
    */
    PORTRAIT: 1,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face up.
    * @constant Phaser.ANGLE_UP
    * @type {integer}
    */
    ANGLE_UP: 270,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face down.
    * @constant Phaser.ANGLE_DOWN
    * @type {integer}
    */
    ANGLE_DOWN: 90,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face left.
    * @constant Phaser.ANGLE_LEFT
    * @type {integer}
    */
    ANGLE_LEFT: 180,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face right.
    * @constant Phaser.ANGLE_RIGHT
    * @type {integer}
    */
    ANGLE_RIGHT: 0,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face north east.
    * @constant Phaser.ANGLE_NORTH_EAST
    * @type {integer}
    */
    ANGLE_NORTH_EAST: 315,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face north west.
    * @constant Phaser.ANGLE_NORTH_WEST
    * @type {integer}
    */
    ANGLE_NORTH_WEST: 225,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face south east.
    * @constant Phaser.ANGLE_SOUTH_EAST
    * @type {integer}
    */
    ANGLE_SOUTH_EAST: 45,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face south west.
    * @constant Phaser.ANGLE_SOUTH_WEST
    * @type {integer}
    */
    ANGLE_SOUTH_WEST: 135,

    /**
    * A constant representing a top-left alignment or position.
    * @constant  Phaser.TOP_LEFT
    * @type {integer}
    */
    TOP_LEFT: 0,

    /**
    * A constant representing a top-center alignment or position.
    * @constant Phaser.TOP_CENTER
    * @type {integer}
    */
    TOP_CENTER: 1,

    /**
    * A constant representing a top-right alignment or position.
    * @constant Phaser.TOP_RIGHT
    * @type {integer}
    */
    TOP_RIGHT: 2,

    /**
    * A constant representing a left-top alignment or position.
    * @constant Phaser.Phaser.LEFT_TOP
    * @type {integer}
    */
    LEFT_TOP: 3,

    /**
    * A constant representing a left-center alignment or position.
    * @constant Phaser.LEFT_TOP
    * @type {integer}
    */
    LEFT_CENTER: 4,

    /**
    * A constant representing a left-bottom alignment or position.
    * @constant Phaser.LEFT_BOTTOM
    * @type {integer}
    */
    LEFT_BOTTOM: 5,

    /**
    * A constant representing a center alignment or position.
    * @constant Phaser.CENTER
    * @type {integer}
    */
    CENTER: 6,

    /**
    * A constant representing a right-top alignment or position.
    * @constant Phaser.RIGHT_TOP
    * @type {integer}
    */
    RIGHT_TOP: 7,

    /**
    * A constant representing a right-center alignment or position.
    * @constant Phaser.RIGHT_CENTER
    * @type {integer}
    */
    RIGHT_CENTER: 8,

    /**
    * A constant representing a right-bottom alignment or position.
    * @constant Phaser.RIGHT_BOTTOM
    * @type {integer}
    */
    RIGHT_BOTTOM: 9,

    /**
    * A constant representing a bottom-left alignment or position.
    * @constant Phaser.BOTTOM_LEFT
    * @type {integer}
    */
    BOTTOM_LEFT: 10,

    /**
    * A constant representing a bottom-center alignment or position.
    * @constant Phaser.BOTTOM_CENTER
    * @type {integer}
    */
    BOTTOM_CENTER: 11,

    /**
    * A constant representing a bottom-right alignment or position.
    * @constant Phaser.BOTTOM_RIGHT
    * @type {integer}
    */
    BOTTOM_RIGHT: 12,

    /**
    * Various blend modes supported by Pixi. See the samples in {@link https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing Canvas Tutorial: Compositing}.
    *
    * IMPORTANT: The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
    *
    * @constant {Object} Phaser.blendModes
    * @property {Number} blendModes.NORMAL - Draws new shapes on top of the existing content. This is the default setting.
    * @property {Number} blendModes.ADD - Where both shapes overlap the color is determined by adding color values.
    * @property {Number} blendModes.MULTIPLY - The pixels of the top layer are multiplied with the corresponding pixel of the bottom layer, making a darker picture.
    * @property {Number} blendModes.SCREEN - The pixels are inverted, multiplied, and inverted again, making a lighter picture.
    * @property {Number} blendModes.OVERLAY
    * @property {Number} blendModes.DARKEN
    * @property {Number} blendModes.LIGHTEN
    * @property {Number} blendModes.COLOR_DODGE
    * @property {Number} blendModes.COLOR_BURN
    * @property {Number} blendModes.HARD_LIGHT
    * @property {Number} blendModes.SOFT_LIGHT
    * @property {Number} blendModes.DIFFERENCE
    * @property {Number} blendModes.EXCLUSION
    * @property {Number} blendModes.HUE
    * @property {Number} blendModes.SATURATION
    * @property {Number} blendModes.COLOR
    * @property {Number} blendModes.LUMINOSITY
    * @static
    */
    blendModes: {
        NORMAL: 0,
        ADD: 1,
        MULTIPLY: 2,
        SCREEN: 3,
        OVERLAY: 4,
        DARKEN: 5,
        LIGHTEN: 6,
        COLOR_DODGE: 7,
        COLOR_BURN: 8,
        HARD_LIGHT: 9,
        SOFT_LIGHT: 10,
        DIFFERENCE: 11,
        EXCLUSION: 12,
        HUE: 13,
        SATURATION: 14,
        COLOR: 15,
        LUMINOSITY: 16
    },

    /**
    * The scale modes that are supported by Pixi.
    *
    * The DEFAULT scale mode affects the default scaling mode of future operations.
    * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
    *
    * @constant {Object} Phaser.scaleModes
    * @property {Number} Phaser.scaleModes.DEFAULT=LINEAR
    * @property {Number} Phaser.scaleModes.LINEAR Smooth scaling
    * @property {Number} Phaser.scaleModes.NEAREST Pixelating scaling
    * @static
    */
    scaleModes: {
        DEFAULT: 0,
        LINEAR: 0,
        NEAREST: 1
    },

    PIXI: PIXI || {},

    //  Used to create IDs for various Pixi objects.
    _UID: 0

};
<commit_msg>Fix docs names for LEFT_TOP and LEFT_CENTER<commit_after>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* @namespace Phaser
*/
var Phaser = Phaser || { // jshint ignore:line

    /**
    * The Phaser version number.
    * @constant Phaser.VERSION
    * @type {string}
    */
    VERSION: '2.13.3',

    /**
    * An array of Phaser game instances.
    * @constant Phaser.GAMES
    * @type {array}
    */
    GAMES: [],

    /**
    * AUTO renderer - picks between WebGL or Canvas based on device.
    * @constant Phaser.AUTO
    * @type {integer}
    */
    AUTO: 0,

    /**
    * Canvas Renderer.
    * @constant Phaser.CANVAS
    * @type {integer}
    */
    CANVAS: 1,

    /**
    * WebGL Renderer.
    * @constant Phaser.WEBGL
    * @type {integer}
    */
    WEBGL: 2,

    /**
    * Headless renderer (not visual output)
    * @constant Phaser.HEADLESS
    * @type {integer}
    */
    HEADLESS: 3,

    /**
    * WebGL Renderer with MultiTexture support enabled.
    * @constant Phaser.WEBGL_MULTI
    * @type {integer}
    */
    WEBGL_MULTI: 4,

    /**
    * Direction constant.
    * @constant Phaser.NONE
    * @type {integer}
    */
    NONE: 0,

    /**
    * Direction constant.
    * @constant Phaser.LEFT
    * @type {integer}
    */
    LEFT: 1,

    /**
    * Direction constant.
    * @constant Phaser.RIGHT
    * @type {integer}
    */
    RIGHT: 2,

    /**
    * Direction constant.
    * @constant Phaser.UP
    * @type {integer}
    */
    UP: 3,

    /**
    * Direction constant.
    * @constant Phaser.DOWN
    * @type {integer}
    */
    DOWN: 4,

    /**
    * Game Object type.
    * @constant Phaser.SPRITE
    * @type {integer}
    */
    SPRITE: 0,

    /**
    * Game Object type.
    * @constant Phaser.BUTTON
    * @type {integer}
    */
    BUTTON: 1,

    /**
    * Game Object type.
    * @constant Phaser.IMAGE
    * @type {integer}
    */
    IMAGE: 2,

    /**
    * Game Object type.
    * @constant Phaser.GRAPHICS
    * @type {integer}
    */
    GRAPHICS: 3,

    /**
    * Game Object type.
    * @constant Phaser.TEXT
    * @type {integer}
    */
    TEXT: 4,

    /**
    * Game Object type.
    * @constant Phaser.TILESPRITE
    * @type {integer}
    */
    TILESPRITE: 5,

    /**
    * Game Object type.
    * @constant Phaser.BITMAPTEXT
    * @type {integer}
    */
    BITMAPTEXT: 6,

    /**
    * Game Object type.
    * @constant Phaser.GROUP
    * @type {integer}
    */
    GROUP: 7,

    /**
    * Game Object type.
    * @constant Phaser.RENDERTEXTURE
    * @type {integer}
    */
    RENDERTEXTURE: 8,

    /**
    * Game Object type.
    * @constant Phaser.TILEMAP
    * @type {integer}
    */
    TILEMAP: 9,

    /**
    * Game Object type.
    * @constant Phaser.TILEMAPLAYER
    * @type {integer}
    */
    TILEMAPLAYER: 10,

    /**
    * Game Object type.
    * @constant Phaser.EMITTER
    * @type {integer}
    */
    EMITTER: 11,

    /**
    * Game Object type.
    * @constant Phaser.POLYGON
    * @type {integer}
    */
    POLYGON: 12,

    /**
    * Game Object type.
    * @constant Phaser.BITMAPDATA
    * @type {integer}
    */
    BITMAPDATA: 13,

    /**
    * Game Object type.
    * @constant Phaser.CANVAS_FILTER
    * @type {integer}
    */
    CANVAS_FILTER: 14,

    /**
    * Game Object type.
    * @constant Phaser.WEBGL_FILTER
    * @type {integer}
    */
    WEBGL_FILTER: 15,

    /**
    * Game Object type.
    * @constant Phaser.ELLIPSE
    * @type {integer}
    */
    ELLIPSE: 16,

    /**
    * Game Object type.
    * @constant Phaser.SPRITEBATCH
    * @type {integer}
    */
    SPRITEBATCH: 17,

    /**
    * Game Object type.
    * @constant Phaser.RETROFONT
    * @type {integer}
    */
    RETROFONT: 18,

    /**
    * Game Object type.
    * @constant Phaser.POINTER
    * @type {integer}
    */
    POINTER: 19,

    /**
    * Game Object type.
    * @constant Phaser.ROPE
    * @type {integer}
    */
    ROPE: 20,

    /**
    * Game Object type.
    * @constant Phaser.CIRCLE
    * @type {integer}
    */
    CIRCLE: 21,

    /**
    * Game Object type.
    * @constant Phaser.RECTANGLE
    * @type {integer}
    */
    RECTANGLE: 22,

    /**
    * Game Object type.
    * @constant Phaser.LINE
    * @type {integer}
    */
    LINE: 23,

    /**
    * Game Object type.
    * @constant Phaser.MATRIX
    * @type {integer}
    */
    MATRIX: 24,

    /**
    * Game Object type.
    * @constant Phaser.POINT
    * @type {integer}
    */
    POINT: 25,

    /**
    * Game Object type.
    * @constant Phaser.ROUNDEDRECTANGLE
    * @type {integer}
    */
    ROUNDEDRECTANGLE: 26,

    /**
    * Game Object type.
    * @constant Phaser.CREATURE
    * @type {integer}
    */
    CREATURE: 27,

    /**
    * Game Object type.
    * @constant Phaser.VIDEO
    * @type {integer}
    */
    VIDEO: 28,

    /**
    * Game Object type.
    * @constant Phaser.PENDING_ATLAS
    * @type {integer}
    */
    PENDING_ATLAS: -1,

    /**
    * A horizontal orientation
    * @constant Phaser.HORIZONTAL
    * @type {integer}
    */
    HORIZONTAL: 0,

    /**
    * A vertical orientation
    * @constant Phaser.VERTICAL
    * @type {integer}
    */
    VERTICAL: 1,

    /**
    * A landscape orientation
    * @constant Phaser.LANDSCAPE
    * @type {integer}
    */
    LANDSCAPE: 0,

    /**
    * A portrait orientation
    * @constant Phaser.PORTRAIT
    * @type {integer}
    */
    PORTRAIT: 1,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face up.
    * @constant Phaser.ANGLE_UP
    * @type {integer}
    */
    ANGLE_UP: 270,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face down.
    * @constant Phaser.ANGLE_DOWN
    * @type {integer}
    */
    ANGLE_DOWN: 90,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face left.
    * @constant Phaser.ANGLE_LEFT
    * @type {integer}
    */
    ANGLE_LEFT: 180,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face right.
    * @constant Phaser.ANGLE_RIGHT
    * @type {integer}
    */
    ANGLE_RIGHT: 0,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face north east.
    * @constant Phaser.ANGLE_NORTH_EAST
    * @type {integer}
    */
    ANGLE_NORTH_EAST: 315,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face north west.
    * @constant Phaser.ANGLE_NORTH_WEST
    * @type {integer}
    */
    ANGLE_NORTH_WEST: 225,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face south east.
    * @constant Phaser.ANGLE_SOUTH_EAST
    * @type {integer}
    */
    ANGLE_SOUTH_EAST: 45,

    /**
    * The Angle (in degrees) a Game Object needs to be set to in order to face south west.
    * @constant Phaser.ANGLE_SOUTH_WEST
    * @type {integer}
    */
    ANGLE_SOUTH_WEST: 135,

    /**
    * A constant representing a top-left alignment or position.
    * @constant  Phaser.TOP_LEFT
    * @type {integer}
    */
    TOP_LEFT: 0,

    /**
    * A constant representing a top-center alignment or position.
    * @constant Phaser.TOP_CENTER
    * @type {integer}
    */
    TOP_CENTER: 1,

    /**
    * A constant representing a top-right alignment or position.
    * @constant Phaser.TOP_RIGHT
    * @type {integer}
    */
    TOP_RIGHT: 2,

    /**
    * A constant representing a left-top alignment or position.
    * @constant Phaser.LEFT_TOP
    * @type {integer}
    */
    LEFT_TOP: 3,

    /**
    * A constant representing a left-center alignment or position.
    * @constant Phaser.LEFT_CENTER
    * @type {integer}
    */
    LEFT_CENTER: 4,

    /**
    * A constant representing a left-bottom alignment or position.
    * @constant Phaser.LEFT_BOTTOM
    * @type {integer}
    */
    LEFT_BOTTOM: 5,

    /**
    * A constant representing a center alignment or position.
    * @constant Phaser.CENTER
    * @type {integer}
    */
    CENTER: 6,

    /**
    * A constant representing a right-top alignment or position.
    * @constant Phaser.RIGHT_TOP
    * @type {integer}
    */
    RIGHT_TOP: 7,

    /**
    * A constant representing a right-center alignment or position.
    * @constant Phaser.RIGHT_CENTER
    * @type {integer}
    */
    RIGHT_CENTER: 8,

    /**
    * A constant representing a right-bottom alignment or position.
    * @constant Phaser.RIGHT_BOTTOM
    * @type {integer}
    */
    RIGHT_BOTTOM: 9,

    /**
    * A constant representing a bottom-left alignment or position.
    * @constant Phaser.BOTTOM_LEFT
    * @type {integer}
    */
    BOTTOM_LEFT: 10,

    /**
    * A constant representing a bottom-center alignment or position.
    * @constant Phaser.BOTTOM_CENTER
    * @type {integer}
    */
    BOTTOM_CENTER: 11,

    /**
    * A constant representing a bottom-right alignment or position.
    * @constant Phaser.BOTTOM_RIGHT
    * @type {integer}
    */
    BOTTOM_RIGHT: 12,

    /**
    * Various blend modes supported by Pixi. See the samples in {@link https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing Canvas Tutorial: Compositing}.
    *
    * IMPORTANT: The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
    *
    * @constant {Object} Phaser.blendModes
    * @property {Number} blendModes.NORMAL - Draws new shapes on top of the existing content. This is the default setting.
    * @property {Number} blendModes.ADD - Where both shapes overlap the color is determined by adding color values.
    * @property {Number} blendModes.MULTIPLY - The pixels of the top layer are multiplied with the corresponding pixel of the bottom layer, making a darker picture.
    * @property {Number} blendModes.SCREEN - The pixels are inverted, multiplied, and inverted again, making a lighter picture.
    * @property {Number} blendModes.OVERLAY
    * @property {Number} blendModes.DARKEN
    * @property {Number} blendModes.LIGHTEN
    * @property {Number} blendModes.COLOR_DODGE
    * @property {Number} blendModes.COLOR_BURN
    * @property {Number} blendModes.HARD_LIGHT
    * @property {Number} blendModes.SOFT_LIGHT
    * @property {Number} blendModes.DIFFERENCE
    * @property {Number} blendModes.EXCLUSION
    * @property {Number} blendModes.HUE
    * @property {Number} blendModes.SATURATION
    * @property {Number} blendModes.COLOR
    * @property {Number} blendModes.LUMINOSITY
    * @static
    */
    blendModes: {
        NORMAL: 0,
        ADD: 1,
        MULTIPLY: 2,
        SCREEN: 3,
        OVERLAY: 4,
        DARKEN: 5,
        LIGHTEN: 6,
        COLOR_DODGE: 7,
        COLOR_BURN: 8,
        HARD_LIGHT: 9,
        SOFT_LIGHT: 10,
        DIFFERENCE: 11,
        EXCLUSION: 12,
        HUE: 13,
        SATURATION: 14,
        COLOR: 15,
        LUMINOSITY: 16
    },

    /**
    * The scale modes that are supported by Pixi.
    *
    * The DEFAULT scale mode affects the default scaling mode of future operations.
    * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
    *
    * @constant {Object} Phaser.scaleModes
    * @property {Number} Phaser.scaleModes.DEFAULT=LINEAR
    * @property {Number} Phaser.scaleModes.LINEAR Smooth scaling
    * @property {Number} Phaser.scaleModes.NEAREST Pixelating scaling
    * @static
    */
    scaleModes: {
        DEFAULT: 0,
        LINEAR: 0,
        NEAREST: 1
    },

    PIXI: PIXI || {},

    //  Used to create IDs for various Pixi objects.
    _UID: 0

};
<|endoftext|>


<commit_before>"use strict";

const blobSymbols = require("./blob-symbols");

module.exports = class Blob {
  constructor() {
    if (!(this instanceof Blob)) {
      throw new TypeError("DOM object constructor cannot be called as a function.");
    }
    const parts = arguments[0];
    const properties = arguments[1];
    if (arguments.length > 0) {
      if (!parts || typeof parts !== "object" || parts instanceof Date || parts instanceof RegExp) {
        throw new TypeError("Blob parts must be objects that are not Dates or RegExps");
      }
    }

    const buffers = [];

    if (parts) {
      const l = Number(parts.length);
      for (let i = 0; i < l; i++) {
        const part = parts[i];
        let buffer;
        if (part instanceof ArrayBuffer) {
          buffer = new Buffer(new Uint8Array(part));
        } else if (part instanceof Blob) {
          buffer = part[blobSymbols.buffer];
        } else if (ArrayBuffer.isView(part)) {
          buffer = new Buffer(new Uint8Array(part.buffer, part.byteOffset, part.byteLength));
        } else if (part instanceof Buffer) {
          buffer = part;
        } else {
          buffer = new Buffer(typeof part === "string" ? part : String(part));
        }
        buffers.push(buffer);
      }
    }

    this[blobSymbols.buffer] = Buffer.concat(buffers);

    this[blobSymbols.type] = properties && properties.type ? String(properties.type).toLowerCase() : "";
    if (!this[blobSymbols.type].match(/^[\u0020-\u007E]*$/)) {
      this[blobSymbols.type] = "";
    }
    this[blobSymbols.lastModified] = properties && properties.lastModified ? properties.lastModified : null;
    this[blobSymbols.closed] = false;
  }
  get size() {
    return this[blobSymbols.buffer].length;
  }
  get type() {
    return this[blobSymbols.type];
  }
  get lastModified() {
    return this[blobSymbols.lastModified];
  }
  slice() {
    const buffer = this[blobSymbols.buffer];
    const slicedBuffer = buffer.slice(
      arguments[0] || 0,
      arguments[1] || this.size
    );
    const blob = new Blob([], { type: arguments[2] || this.type });
    blob[blobSymbols.buffer] = slicedBuffer;
    return blob;
  }
  close() {
    this[blobSymbols.closed] = true;
  }
  toString() {
    return "[object Blob]";
  }
};
<commit_msg>Make File's lastModified return the current time as the default<commit_after>"use strict";

const blobSymbols = require("./blob-symbols");

module.exports = class Blob {
  constructor() {
    if (!(this instanceof Blob)) {
      throw new TypeError("DOM object constructor cannot be called as a function.");
    }
    const parts = arguments[0];
    const properties = arguments[1];
    if (arguments.length > 0) {
      if (!parts || typeof parts !== "object" || parts instanceof Date || parts instanceof RegExp) {
        throw new TypeError("Blob parts must be objects that are not Dates or RegExps");
      }
    }

    const buffers = [];

    if (parts) {
      const l = Number(parts.length);
      for (let i = 0; i < l; i++) {
        const part = parts[i];
        let buffer;
        if (part instanceof ArrayBuffer) {
          buffer = new Buffer(new Uint8Array(part));
        } else if (part instanceof Blob) {
          buffer = part[blobSymbols.buffer];
        } else if (ArrayBuffer.isView(part)) {
          buffer = new Buffer(new Uint8Array(part.buffer, part.byteOffset, part.byteLength));
        } else if (part instanceof Buffer) {
          buffer = part;
        } else {
          buffer = new Buffer(typeof part === "string" ? part : String(part));
        }
        buffers.push(buffer);
      }
    }

    this[blobSymbols.buffer] = Buffer.concat(buffers);

    this[blobSymbols.type] = properties && properties.type ? String(properties.type).toLowerCase() : "";
    if (!this[blobSymbols.type].match(/^[\u0020-\u007E]*$/)) {
      this[blobSymbols.type] = "";
    }
    this[blobSymbols.lastModified] = properties && properties.lastModified ? properties.lastModified : null;
    this[blobSymbols.closed] = false;
  }
  get size() {
    return this[blobSymbols.buffer].length;
  }
  get type() {
    return this[blobSymbols.type];
  }
  get lastModified() {
    return this[blobSymbols.lastModified] || Date.now();
  }
  slice() {
    const buffer = this[blobSymbols.buffer];
    const slicedBuffer = buffer.slice(
      arguments[0] || 0,
      arguments[1] || this.size
    );
    const blob = new Blob([], { type: arguments[2] || this.type });
    blob[blobSymbols.buffer] = slicedBuffer;
    return blob;
  }
  close() {
    this[blobSymbols.closed] = true;
  }
  toString() {
    return "[object Blob]";
  }
};
<|endoftext|>


<commit_before>var juice = require('juice'),
    Template = require('Template').Template,
    Validation = require('juice/validation').Validation;

var Application = function Application() {
  this.actions = {};
  this.controllers = {};
  this.helpers = {
    source : function( x ) { return x.toSource(); }
  };

  Object.defineProperty(this, 'setupRun', { writable: true, value: false });
}

exports.Application = Application;

Application.prototype.setup = function setup() {
  if (this.setupRun)
    return;

  for (k in this.actions) {
    this.actions[ k ] = this.buildAction( k, this.actions[ k ] );
  }

  this.setupRun = true;
}

Application.prototype.run = function run(env) {
  var app = this;
  try {
    // Setup if we haven't already
    this.setup();

    var ctx = new juice.Context(env, app);

    // Dispatch the url
    var url = env.pathInfo;
    ctx.format = env.headers.accept.match( 'application/json' ) ? 'json' : 'html';
    for (k in ctx.actions) {
      let info = ctx.actions[ k ];

      let re = info.__matcher;

      var res = re(url);

      if (!res)
        continue;

      // We matched! dispatch to the action

      // We assume that re is infact a regexp, so res is an array of the form:
      //
      //  [ 'whole matched text', 'capture1', ... 'captureN' ]
      //
      //  We dont want the whole matched text.
      res.shift();

      return ctx.runAction(res, info);
    }

    // 404!
    return app.notFound(env);
  }
  catch (e) {
    return app.handleError(env, e);
  }
}

/**
 * CSS for built in 404 and 500 error pages.
 */
Application.prototype.errorCss = [
'    * { margin: 0; padding: 0; }',
'    body { background: #eee; color: #000; font: 200 87.5%/1.5 "Lucida Grande", Calibri, sans-serif; }',
'    #overview { background: #79d108; border-bottom: 1.5em solid #90e71d; padding: 1.5em; }',
'    #details { padding: 1.5em; }',
'    h1 { font-size: 1.5em; font-weight: 200; line-height: 1; }',
'    dl { font-family: Consolas, monospace; margin-top: 1.5em; overflow: auto; }',
'    dt { clear: both; float: left; margin-right: 1em; text-align: right; width: 9em; }',
'    dd { float: left; }',
'    ol { font-family: Consolas, monospace; margin: 1.5em; }',
'    code { background: #a6f540; color: #000; font-family: Consolas, monospace; font-size: 1em; padding: 0.125em; }',
'    ol.stacktrace {display: table }',
'    ol.stacktrace li { display: table-row; }',
'    ol.stacktrace span { display: table-cell; }',
'    ol.stacktrace span:first-child { padding-right: 1em; text-align: right; }',
'    ol.stacktrace span:last-child { padding-left: 1em; }'
].join('\n');

/**
 * Template to render when no actions match
 */
Application.prototype.notFoundTemplate = <><![CDATA[<!DOCTYPE html>

  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title>Page not found</title>

Page not found — 404

URL:

[% url %]

Method:

[%env.requestMethod%]

Juice tried to match the URL [% env.pathInfo %] against the
    following patterns, but failed to find a match.

[% FOREACH k IN actions %]

[% k.key %]

[% END -%]

]]></> + "";

Application.prototype.notFound = function(env) {

  var url = env.scheme
          + "://"
          + env.serverName
          + ":"
          + env.serverPort
          + env.scriptName
          + env.pathInfo;

  var tt = new Template();
  return {
    status: 404,
    headers: { contentType : 'text/html' },
    body: tt.process(
      this.notFoundTemplate,
      { url: url,
        actions: this.actions,
        env: env,
        css: this.errorCss
      }
    )
  };
}

/**
 * Template to render when handling an action throws an error
 */
Application.prototype.errorTemplate = <><![CDATA[<!DOCTYPE html>

  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title>Server error</title>

[% errorType %] — 500

Error message:

[% e.message || e %]

URL:

[% url %]

Method:

[% env.requestMethod %]

[% IF e.fileName %]

File:

[%e.fileName%]

[% END;
   IF e.lineNumber %]

Line number:

[%e.lineNumber%]

[% END %]

[% IF !frames.length %]

Sorry, no stack trace is avilable

[% ELSE %]

[% FOREACH f IN frames; %]

[%
              IF f.function;
                "$f.function";
              ELSE;
                "Unknown";
              END -%]
          @

            [%-
              IF f.fileName;
                "$f.fileName";
              ELSE;
                "Unknown";
              END;
              IF f.lineNumber;
                ", $f.lineNumber";
              END;
          -%]

[% END %]

[% END %]

]]></> + "";

Application.prototype.handleError = function(env, e) {

  var url = env.scheme
          + "://"
          + env.serverName
          + ":"
          + env.serverPort
          + env.scriptName
          + env.pathInfo;

  var errorType = (e.constructor ? e.constructor.name : false) || "Unknown Error";
  var msg = e.message || e;

  var tt = new Template();
  var data = {
    url: url,
    e: e,
    errorType: errorType,
    env: env,
    css: this.errorCss
  };

  if (e.stack)
    data.frames = juice.Utils.splitErrorStack(e.stack);

  return {
    status: 500,
    headers: { contentType : 'text/html' },
    body: tt.process( this.errorTemplate, data )
  };
}

Application.prototype.getAction = function( context, name ) {
  for ( var [k,v] in Iterator( name.split( '.' ) ) ) {
    if ( v in context == false )
      throw new TypeError( name + " cannot be found in the controller" );
    context = context[ v ];
  }
  return context;
}

Application.prototype.buildAction = function( url, action ) {
  if ( typeof action == "function" || typeof action == "string" )
    action = { action : action };

  if ( typeof action.action == "string" ) {
    if ( ! action.render && ! action.redirect )
      action.render = action.action.replace( '.', '/', 'g' ) + '.tt';
    action.action = this.getAction( this.controllers, action.action );
  }

  if ( "__matcher" in action && typeof action.__matcher != "function" )
    throw new TypeError( "action.__matcher is not a function" );

  if ( ! "__matcher" in action == false )
    action.__matcher = new RegExp( "^" + url + "$" );

  return action;
}

Application.prototype.validate = function( form, params ) {
  var data = {};
  // loop through |params|, stick values into |field| key of the spec.
  for (let [field, spec] in Iterator(form)) {
    data[field] = juice.Utils.clone(spec, true);
    // use input: key if it exists, else take key in fields: struct
    if ("input" in spec) data[field].value = params[spec.input];
    else if (field in params) data[field].value = params[field];
  }

  return (new Validation(data)).validate();
}

<commit_msg>Make sure we don't die when there is no Accept header<commit_after>var juice = require('juice'),
    Template = require('Template').Template,
    Validation = require('juice/validation').Validation;

var Application = function Application() {
  this.actions = {};
  this.controllers = {};
  this.helpers = {
    source : function( x ) { return x.toSource(); }
  };

  Object.defineProperty(this, 'setupRun', { writable: true, value: false });
}

exports.Application = Application;

Application.prototype.setup = function setup() {
  if (this.setupRun)
    return;

  for (k in this.actions) {
    this.actions[ k ] = this.buildAction( k, this.actions[ k ] );
  }

  this.setupRun = true;
}

Application.prototype.run = function run(env) {
  var app = this;
  try {
    // Setup if we haven't already
    this.setup();

    var ctx = new juice.Context(env, app);

    // Dispatch the url
    var url = env.pathInfo;
    ctx.format = env.headers.accept &&
                 env.headers.accept.match( 'application/json' )
               ? 'json'
               : 'html';

    for (k in ctx.actions) {
      let info = ctx.actions[ k ];

      let re = info.__matcher;

      var res = re(url);

      if (!res)
        continue;

      // We matched! dispatch to the action

      // We assume that re is infact a regexp, so res is an array of the form:
      //
      //  [ 'whole matched text', 'capture1', ... 'captureN' ]
      //
      //  We dont want the whole matched text.
      res.shift();

      return ctx.runAction(res, info);
    }

    // 404!
    return app.notFound(env);
  }
  catch (e) {
    return app.handleError(env, e);
  }
}

/**
 * CSS for built in 404 and 500 error pages.
 */
Application.prototype.errorCss = [
'    * { margin: 0; padding: 0; }',
'    body { background: #eee; color: #000; font: 200 87.5%/1.5 "Lucida Grande", Calibri, sans-serif; }',
'    #overview { background: #79d108; border-bottom: 1.5em solid #90e71d; padding: 1.5em; }',
'    #details { padding: 1.5em; }',
'    h1 { font-size: 1.5em; font-weight: 200; line-height: 1; }',
'    dl { font-family: Consolas, monospace; margin-top: 1.5em; overflow: auto; }',
'    dt { clear: both; float: left; margin-right: 1em; text-align: right; width: 9em; }',
'    dd { float: left; }',
'    ol { font-family: Consolas, monospace; margin: 1.5em; }',
'    code { background: #a6f540; color: #000; font-family: Consolas, monospace; font-size: 1em; padding: 0.125em; }',
'    ol.stacktrace {display: table }',
'    ol.stacktrace li { display: table-row; }',
'    ol.stacktrace span { display: table-cell; }',
'    ol.stacktrace span:first-child { padding-right: 1em; text-align: right; }',
'    ol.stacktrace span:last-child { padding-left: 1em; }'
].join('\n');

/**
 * Template to render when no actions match
 */
Application.prototype.notFoundTemplate = <><![CDATA[<!DOCTYPE html>

  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title>Page not found</title>

Page not found — 404

URL:

[% url %]

Method:

[%env.requestMethod%]

Juice tried to match the URL [% env.pathInfo %] against the
    following patterns, but failed to find a match.

[% FOREACH k IN actions %]

[% k.key %]

[% END -%]

]]></> + "";

Application.prototype.notFound = function(env) {

  var url = env.scheme
          + "://"
          + env.serverName
          + ":"
          + env.serverPort
          + env.scriptName
          + env.pathInfo;

  var tt = new Template();
  return {
    status: 404,
    headers: { contentType : 'text/html' },
    body: tt.process(
      this.notFoundTemplate,
      { url: url,
        actions: this.actions,
        env: env,
        css: this.errorCss
      }
    )
  };
}

/**
 * Template to render when handling an action throws an error
 */
Application.prototype.errorTemplate = <><![CDATA[<!DOCTYPE html>

  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <title>Server error</title>

[% errorType %] — 500

Error message:

[% e.message || e %]

URL:

[% url %]

Method:

[% env.requestMethod %]

[% IF e.fileName %]

File:

[%e.fileName%]

[% END;
   IF e.lineNumber %]

Line number:

[%e.lineNumber%]

[% END %]

[% IF !frames.length %]

Sorry, no stack trace is avilable

[% ELSE %]

[% FOREACH f IN frames; %]

[%
              IF f.function;
                "$f.function";
              ELSE;
                "Unknown";
              END -%]
          @

            [%-
              IF f.fileName;
                "$f.fileName";
              ELSE;
                "Unknown";
              END;
              IF f.lineNumber;
                ", $f.lineNumber";
              END;
          -%]

[% END %]

[% END %]

]]></> + "";

Application.prototype.handleError = function(env, e) {

  var url = env.scheme
          + "://"
          + env.serverName
          + ":"
          + env.serverPort
          + env.scriptName
          + env.pathInfo;

  var errorType = (e.constructor ? e.constructor.name : false) || "Unknown Error";
  var msg = e.message || e;

  var tt = new Template();
  var data = {
    url: url,
    e: e,
    errorType: errorType,
    env: env,
    css: this.errorCss
  };

  if (e.stack)
    data.frames = juice.Utils.splitErrorStack(e.stack);

  return {
    status: 500,
    headers: { contentType : 'text/html' },
    body: tt.process( this.errorTemplate, data )
  };
}

Application.prototype.getAction = function( context, name ) {
  for ( var [k,v] in Iterator( name.split( '.' ) ) ) {
    if ( v in context == false )
      throw new TypeError( name + " cannot be found in the controller" );
    context = context[ v ];
  }
  return context;
}

Application.prototype.buildAction = function( url, action ) {
  if ( typeof action == "function" || typeof action == "string" )
    action = { action : action };

  if ( typeof action.action == "string" ) {
    if ( ! action.render && ! action.redirect )
      action.render = action.action.replace( '.', '/', 'g' ) + '.tt';
    action.action = this.getAction( this.controllers, action.action );
  }

  if ( "__matcher" in action && typeof action.__matcher != "function" )
    throw new TypeError( "action.__matcher is not a function" );

  if ( ! "__matcher" in action == false )
    action.__matcher = new RegExp( "^" + url + "$" );

  return action;
}

Application.prototype.validate = function( form, params ) {
  var data = {};
  // loop through |params|, stick values into |field| key of the spec.
  for (let [field, spec] in Iterator(form)) {
    data[field] = juice.Utils.clone(spec, true);
    // use input: key if it exists, else take key in fields: struct
    if ("input" in spec) data[field].value = params[spec.input];
    else if (field in params) data[field].value = params[field];
  }

  return (new Validation(data)).validate();
}

<|endoftext|>


<commit_before>(function (root, factory) {
    if ( typeof define === 'function' && define.amd ) {
        define([], function () {
            return factory(root);
        });
    } else if ( typeof exports === 'object' ) {
        module.exports = factory(root);
    } else {
        root.JustifiedColumns = factory(root);
    }
})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, function (window) {
    var DEFAULT_THROTTLE = 25,
        WRAPPER_CLASS = 'justified-columns-wrapper',
        NEEDS_WRAPPER = document.documentElement.style.objectFit === undefined;

    function pxAsNum(px) {
        return 1 * px.split('px')[0];
    }

    function throttle(fn, threshhold, scope) {
        threshhold || (threshhold = 250);
        var last,
            deferTimer;

        return function() {
            var context = scope || this;

            var now = +new Date,
                args = arguments;

            if (last && now < last + threshhold) {
                clearTimeout(deferTimer);
                deferTimer = setTimeout(function() {
                    last = now;
                    fn.apply(context, args);
                }, threshhold);
            } else {
                last = now;
                fn.apply(context, args);
            }
        };
    }

    function Justifier(config) {
        this.setGrid(config.grid || config);
        this.stretchSelector = config.stretch || 'img';
        this.shrink = config.shrink || false; //or 'shrink' (column-justify);
        this.autoResize = config.hasOwnProperty('autoResize') ? config.autoResize : true;
        this.grid.Justifier = this;
        this.boundReset = config.throttle === 0 ? this.reset.bind(this) : throttle(this.reset.bind(this), config.throttle || DEFAULT_THROTTLE);
        this.boundJustify = config.throttle === 0 ? this.justify.bind(this) : throttle(this.justify.bind(this), config.throttle || DEFAULT_THROTTLE);
        this.enable();
    }

    Justifier.prototype.setGrid = function(grid) {
        if (typeof grid === 'string') {
            this.grid = document.querySelector(grid);
        } else {
            this.grid = grid.jquery ? grid[0] : grid;
        }
    };

    Justifier.prototype.justify = function() {
        var self = this,
            refColComparator = Math[this.shrink ? 'min' : 'max'],
            columns = [],
            currentColumn = -1,
            referenceColumn = this.shrink ? Number.POSITIVE_INFINITY : 0,
            currentOffset;

        //sort the grid into columns based on offsetLeft; cache heights
        [].slice.call(this.grid.children).forEach(function(gridItem) {
            var gridItemStyle = window.getComputedStyle(gridItem);
            var gridItemOffsetLeft = gridItem.offsetLeft; //saves like 40% of execution time to cache this
            if (gridItemOffsetLeft !== currentOffset) {
                if (currentColumn !== -1) referenceColumn = refColComparator(columns[currentColumn].height, referenceColumn);
                currentColumn++;
                currentOffset = gridItemOffsetLeft;
                columns.push({
                    items: [],
                    height: pxAsNum(gridItemStyle.getPropertyValue('margin-top')),
                    totalImagesHeight: 0
                });
            }
            gridItem.stretchable = gridItem.querySelector(self.stretchSelector);
            if (gridItem.stretchable) {
                gridItem.stretchableHeight = gridItem.stretchable.offsetHeight;
            }
            columns[currentColumn].items.push(gridItem);
            columns[currentColumn].height += (gridItem.offsetHeight + pxAsNum(gridItemStyle.getPropertyValue('margin-bottom')));
            columns[currentColumn].totalImagesHeight += (gridItem.stretchableHeight ? gridItem.stretchableHeight : 0);
        });
        referenceColumn = refColComparator(columns[currentColumn].height, referenceColumn); //last column could be reference, too

        //modify height of "stretchable" items (if present) in each of the non-reference-columns' items
        columns.forEach(function(column, colIndex) {
            if (column.height < referenceColumn || (self.shrink && column.height > referenceColumn)) {
                var columnDelta = referenceColumn - column.height;
                var stretchedImagesDelta = 0;
                column.items.forEach(function(gridItem, itemIndex) {
                    var stretchable = gridItem.querySelector(self.stretchSelector);
                    if (stretchable) {
                        var stretchablePercent = columnDelta / column.totalImagesHeight,
                            stretchableDelta = gridItem.stretchableHeight * stretchablePercent,
                            newHeight = gridItem.stretchableHeight + stretchableDelta,
                            heightProperty = (self.shrink ? 'max' : 'min') + 'Height';
                        if (stretchable.nodeName !== 'IMG') {
                            // not an image, so just set max/min-height and call it a day
                            stretchable.style[heightProperty] = newHeight + 'px';
                        } else if (!NEEDS_WRAPPER) {
                            // can rely on objectFit, so set the max/min-height, then use objectFit to handle resize + overflow
                            stretchable.style[heightProperty] = newHeight + 'px';
                            stretchable.style.objectFit = 'cover';
                            stretchable.style.objectPosition = 'center';
                        } else {
                            //it's an IMG and we're in older IE, ugh
                            if (stretchable.parentNode.className !== WRAPPER_CLASS) {
                                //create overflow wrapper
                                var wrapper = document.createElement('DIV');
                                wrapper.className = WRAPPER_CLASS;
                                wrapper.style.overflow = 'hidden';
                                wrapper.style.fontSize = 0; //otherwise IE will add just a liiiiitle bit of extra space
                                wrapper.style[heightProperty] = newHeight + 'px';
                                stretchable.parentNode.replaceChild(wrapper, stretchable);
                                wrapper.appendChild(stretchable);
                            }
                            //set wrapper max/min-height, position within wrapper
                            if (self.shrink) {
                                var sOffset = 0.5 * (newHeight - gridItem.stretchableHeight);
                                stretchable.style.marginTop = sOffset + 'px';
                                stretchable.style.marginBottom = -1 * sOffset + 'px';
                            } else {
                                var sWidth = stretchable.offsetWidth,
                                    newWidth = sWidth * (newHeight / gridItem.stretchableHeight);
                                stretchable.style.minHeight = newHeight + 'px';
                                stretchable.style.minWidth = newWidth + 'px';
                                stretchable.style.marginLeft = -0.5 * (newWidth - sWidth) + 'px';
                            }
                        }
                        stretchedImagesDelta += stretchableDelta;
                    }
                });
                column.height += stretchedImagesDelta;
                column.totalImagesHeight += stretchedImagesDelta;
            }
        });

        //Fix the stupid IE10/11 interactions between column-count grid and height/max-height children
        if (NEEDS_WRAPPER) {
            var resizeHeight = referenceColumn + 2,
                gridStyle = getComputedStyle(this.grid);
            if (gridStyle.boxSizing === 'border-box') {
                ['paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'].forEach(function(spacer) {
                    resizeHeight += pxAsNum(gridStyle[spacer]);
                });
            }
            if (Math.abs(this.grid.offsetHeight - resizeHeight) >= 2) {
                this.grid.style.height = resizeHeight + 'px';
            }
        }

    };

    Justifier.prototype.reset = function() {
        [].slice.call(this.grid.querySelectorAll(this.stretchSelector)).forEach(function(s) {
            s.style.maxHeight = '';
            s.style.minHeight = '';
            if (NEEDS_WRAPPER) {
                s.style.minWidth = '';
                s.style.marginLeft = '';
                s.style.marginTop = '';
            } else {
                s.style.objectFit = '';
                s.style.objectPosition = '';
            }
        });
        if (NEEDS_WRAPPER) {
            [].slice.call(this.grid.getElementsByClassName(WRAPPER_CLASS)).forEach(function(wrapper) {
                wrapper.parentNode.replaceChild(wrapper.firstChild, wrapper);
            });
            this.grid.style.height = '';
        }
    };

    Justifier.prototype.enable = function() {
        if (this.autoResize) {
            window.addEventListener('resize', this.boundReset);
            window.addEventListener('resize', this.boundJustify);
        }
        this.justify();
    };

    Justifier.prototype.disable = function() {
        if (this.autoResize) {
            window.removeEventListener('resize', this.boundReset);
            window.removeEventListener('resize', this.boundJustify);
        }
        this.reset();
    };

    return Justifier;
});
<commit_msg>GLBE-6005 Rename Justifier to JustifiedColumns. Consistency of naming across platforms<commit_after>(function (root, factory) {
    if ( typeof define === 'function' && define.amd ) {
        define([], function () {
            return factory(root);
        });
    } else if ( typeof exports === 'object' ) {
        module.exports = factory(root);
    } else {
        root.JustifiedColumns = factory(root);
    }
})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, function (window) {
    var DEFAULT_THROTTLE = 25,
        WRAPPER_CLASS = 'justified-columns-wrapper',
        NEEDS_WRAPPER = document.documentElement.style.objectFit === undefined;

    function pxAsNum(px) {
        return 1 * px.split('px')[0];
    }

    function throttle(fn, threshhold, scope) {
        threshhold || (threshhold = 250);
        var last,
            deferTimer;

        return function() {
            var context = scope || this;

            var now = +new Date,
                args = arguments;

            if (last && now < last + threshhold) {
                clearTimeout(deferTimer);
                deferTimer = setTimeout(function() {
                    last = now;
                    fn.apply(context, args);
                }, threshhold);
            } else {
                last = now;
                fn.apply(context, args);
            }
        };
    }

    function JustifiedColumns(config) {
        this.setGrid(config.grid || config);
        this.stretchSelector = config.stretch || 'img';
        this.shrink = config.shrink || false; //or 'shrink' (column-justify);
        this.autoResize = config.hasOwnProperty('autoResize') ? config.autoResize : true;
        this.grid.JustifiedColumns = this;
        this.boundReset = config.throttle === 0 ? this.reset.bind(this) : throttle(this.reset.bind(this), config.throttle || DEFAULT_THROTTLE);
        this.boundJustify = config.throttle === 0 ? this.justify.bind(this) : throttle(this.justify.bind(this), config.throttle || DEFAULT_THROTTLE);
        this.enable();
    }

    JustifiedColumns.prototype.setGrid = function(grid) {
        if (typeof grid === 'string') {
            this.grid = document.querySelector(grid);
        } else {
            this.grid = grid.jquery ? grid[0] : grid;
        }
    };

    JustifiedColumns.prototype.justify = function() {
        var self = this,
            refColComparator = Math[this.shrink ? 'min' : 'max'],
            columns = [],
            currentColumn = -1,
            referenceColumn = this.shrink ? Number.POSITIVE_INFINITY : 0,
            currentOffset;

        //sort the grid into columns based on offsetLeft; cache heights
        [].slice.call(this.grid.children).forEach(function(gridItem) {
            var gridItemStyle = window.getComputedStyle(gridItem);
            var gridItemOffsetLeft = gridItem.offsetLeft; //saves like 40% of execution time to cache this
            if (gridItemOffsetLeft !== currentOffset) {
                if (currentColumn !== -1) referenceColumn = refColComparator(columns[currentColumn].height, referenceColumn);
                currentColumn++;
                currentOffset = gridItemOffsetLeft;
                columns.push({
                    items: [],
                    height: pxAsNum(gridItemStyle.getPropertyValue('margin-top')),
                    totalImagesHeight: 0
                });
            }
            gridItem.stretchable = gridItem.querySelector(self.stretchSelector);
            if (gridItem.stretchable) {
                gridItem.stretchableHeight = gridItem.stretchable.offsetHeight;
            }
            columns[currentColumn].items.push(gridItem);
            columns[currentColumn].height += (gridItem.offsetHeight + pxAsNum(gridItemStyle.getPropertyValue('margin-bottom')));
            columns[currentColumn].totalImagesHeight += (gridItem.stretchableHeight ? gridItem.stretchableHeight : 0);
        });
        referenceColumn = refColComparator(columns[currentColumn].height, referenceColumn); //last column could be reference, too

        //modify height of "stretchable" items (if present) in each of the non-reference-columns' items
        columns.forEach(function(column, colIndex) {
            if (column.height < referenceColumn || (self.shrink && column.height > referenceColumn)) {
                var columnDelta = referenceColumn - column.height;
                var stretchedImagesDelta = 0;
                column.items.forEach(function(gridItem, itemIndex) {
                    var stretchable = gridItem.querySelector(self.stretchSelector);
                    if (stretchable) {
                        var stretchablePercent = columnDelta / column.totalImagesHeight,
                            stretchableDelta = gridItem.stretchableHeight * stretchablePercent,
                            newHeight = gridItem.stretchableHeight + stretchableDelta,
                            heightProperty = (self.shrink ? 'max' : 'min') + 'Height';
                        if (stretchable.nodeName !== 'IMG') {
                            // not an image, so just set max/min-height and call it a day
                            stretchable.style[heightProperty] = newHeight + 'px';
                        } else if (!NEEDS_WRAPPER) {
                            // can rely on objectFit, so set the max/min-height, then use objectFit to handle resize + overflow
                            stretchable.style[heightProperty] = newHeight + 'px';
                            stretchable.style.objectFit = 'cover';
                            stretchable.style.objectPosition = 'center';
                        } else {
                            //it's an IMG and we're in older IE, ugh
                            if (stretchable.parentNode.className !== WRAPPER_CLASS) {
                                //create overflow wrapper
                                var wrapper = document.createElement('DIV');
                                wrapper.className = WRAPPER_CLASS;
                                wrapper.style.overflow = 'hidden';
                                wrapper.style.fontSize = 0; //otherwise IE will add just a liiiiitle bit of extra space
                                wrapper.style[heightProperty] = newHeight + 'px';
                                stretchable.parentNode.replaceChild(wrapper, stretchable);
                                wrapper.appendChild(stretchable);
                            }
                            //set wrapper max/min-height, position within wrapper
                            if (self.shrink) {
                                var sOffset = 0.5 * (newHeight - gridItem.stretchableHeight);
                                stretchable.style.marginTop = sOffset + 'px';
                                stretchable.style.marginBottom = -1 * sOffset + 'px';
                            } else {
                                var sWidth = stretchable.offsetWidth,
                                    newWidth = sWidth * (newHeight / gridItem.stretchableHeight);
                                stretchable.style.minHeight = newHeight + 'px';
                                stretchable.style.minWidth = newWidth + 'px';
                                stretchable.style.marginLeft = -0.5 * (newWidth - sWidth) + 'px';
                            }
                        }
                        stretchedImagesDelta += stretchableDelta;
                    }
                });
                column.height += stretchedImagesDelta;
                column.totalImagesHeight += stretchedImagesDelta;
            }
        });

        //Fix the stupid IE10/11 interactions between column-count grid and height/max-height children
        if (NEEDS_WRAPPER) {
            var resizeHeight = referenceColumn + 2,
                gridStyle = getComputedStyle(this.grid);
            if (gridStyle.boxSizing === 'border-box') {
                ['paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'].forEach(function(spacer) {
                    resizeHeight += pxAsNum(gridStyle[spacer]);
                });
            }
            if (Math.abs(this.grid.offsetHeight - resizeHeight) >= 2) {
                this.grid.style.height = resizeHeight + 'px';
            }
        }

    };

    JustifiedColumns.prototype.reset = function() {
        [].slice.call(this.grid.querySelectorAll(this.stretchSelector)).forEach(function(s) {
            s.style.maxHeight = '';
            s.style.minHeight = '';
            if (NEEDS_WRAPPER) {
                s.style.minWidth = '';
                s.style.marginLeft = '';
                s.style.marginTop = '';
            } else {
                s.style.objectFit = '';
                s.style.objectPosition = '';
            }
        });
        if (NEEDS_WRAPPER) {
            [].slice.call(this.grid.getElementsByClassName(WRAPPER_CLASS)).forEach(function(wrapper) {
                wrapper.parentNode.replaceChild(wrapper.firstChild, wrapper);
            });
            this.grid.style.height = '';
        }
    };

    JustifiedColumns.prototype.enable = function() {
        if (this.autoResize) {
            window.addEventListener('resize', this.boundReset);
            window.addEventListener('resize', this.boundJustify);
        }
        this.justify();
    };

    JustifiedColumns.prototype.disable = function() {
        if (this.autoResize) {
            window.removeEventListener('resize', this.boundReset);
            window.removeEventListener('resize', this.boundJustify);
        }
        this.reset();
    };

    return JustifiedColumns;
});
<|endoftext|>


<commit_before>"use strict";

// Grab dependencies required by the middleware.
var util         = require('util'),
    keen         = require('keen.io'),
    // See [connect-friendwares](http://github.com/sebinsua/connect-friendwares).
    friendwares  = require('connect-friendwares'),
    EventEmitter = require('events').EventEmitter;

// See [lib/core/helpers](http://sebinsua.github.io/express-keenio/helpers.html).
var helpers       = require('./core/helpers'),
    // See [lib/core/options-parser](http://sebinsua.github.io/express-keenio/options-parser.html).
    optionsParser = require('./core/options-parser');

    // See [lib/routes](http://sebinsua.github.io/express-keenio/routes.html).
var RoutesModule          = require('./routes'),
    // See [lib/parse/proxy-response](http://sebinsua.github.io/express-keenio/proxy-response.html).
    ProxyResponseModule   = require('./parse/proxy-response'),
    // See [lib/parse/request](http://sebinsua.github.io/express-keenio/request.html).
    RequestModule         = require('./parse/request'),
    // See [lib/parse/identify](http://sebinsua.github.io/express-keenio/identify.html).
    IdentifyModule        = require('./parse/identify'),
     // See [lib/event-collection](http://sebinsua.github.io/express-keenio/event-collection.html).
    EventCollectionModule = require('./event-collection'),
    // See [lib/keen-event](http://sebinsua.github.io/express-keenio/keen-event.html).
    KeenEventModule       = require('./keen-event');

// Construct the middleware uninitialised with some default events.
function KeenioMiddleware () {
  this.options = {};
  this.handlers = {};
  this.initialized = false;

  // This initialises an internal `_ee` EventEmitter with the events: error, info, track and flush.
  this._ee = helpers.setDefaultEvents(new EventEmitter(), ['error', 'info', 'track', 'flush']);
}

// Bind the EventEmitter methods to the middleware.
helpers.forEach(EventEmitter.prototype, function (fn, key) {
  KeenioMiddleware.prototype[key] = function () {
    this._ee[key].apply(this, arguments);
  };
});

// Before the middleware can be used it must have its configuration passed into it.
KeenioMiddleware.prototype.configure = function (options) {
  this.options = optionsParser.parse(options);

  this._configureHandlers(this.options);

  this.keenClient = keen.configure(options.client);
  this.initialized = true;
  this.emit('initialized');
};

// A method may be called to check whether the middleware has had configure() called before
// it is used. It will throw an exception if the middleware has not yet been initialised.
KeenioMiddleware.prototype.checkInitialized = function () {
  if (!this.initialized) {
    throw new Error('express-keenio middleware must be configured before use. Please call ' +
                    'keenio-middleware#configure(options).');
  }
  return true;
};

// This method allows us to check that the middleware is in use by Express and not
// just Connect. It does this by checking for the existence of a middleware called
// 'expressInit'.
//
// The purpose of this is that the middleware requires some behaviours given by Express,
// particularly relating to route definitions.
KeenioMiddleware.prototype.isMiddlewareUsedByExpress = function (app) {
  return friendwares(app).has('expressInit');
};

// This is exposed in order that an instance of the middleware can be passed directly into
// app.use() - which either executes a function, or looks for the function at an object's
// `handle` property.
KeenioMiddleware.prototype.handle = function keenioHandler(req, res, next) {
  var middleware = this.handleAll();
  middleware(req, res, next);
};

KeenioMiddleware.prototype.handleAll = function () {
  this.checkInitialized();

  return this._generateHandler({});
};

// `trackRoute()` allows the middlewares to be attached to specific [Express.js](http://expressjs.com)
// routes and to explicitly define their event collection name.
KeenioMiddleware.prototype.trackRoute = function (eventCollectionName, eventTag) {
  this.checkInitialized();

  return this._generateHandler({
    eventCollectionName: eventCollectionName,
    tag: eventTag
  });
};

// This is an internal method which generates the middleware handler when given a routeConfig.
// The routeConfig must be empty if we are using the middleware against the whole app.
KeenioMiddleware.prototype._generateHandler = function (routeConfig) {
  var self = this;

  return function keenioHandler(req, res, next) {
    // We ensure that the middleware is being used by an express app.
    if (!req.app || !self.isMiddlewareUsedByExpress(req.app)) {
      self.emit('error', 'Currently this middleware is only supported by Express.js.');
      return next();
    }

    // We swap the methods on a response object with proxied versions, and respond
    // with a parsedResponseData object that is still in scope of the res object's proxied
    // response methods (closures.)
    // This may not be the most elegant solution but it works.
    var parsedResponseData = self.proxyResponseHandler.proxyResponseObject(res);

    // We setup a function that will be called when a response is finished, and pass
    // in the request object, routeConfig, and parsedResponseData object.
    // The parsedResponseData object at this point contains no data, but since it is
    // still in the scope of the proxied response object it will contain response data
    // by the time the response has had its `finish` event emitted.
    res.on("finish", self._finishResponse(req, parsedResponseData, routeConfig));

    return next();
  };
};

KeenioMiddleware.prototype._finishResponse = function (req, _parsedResponseData, routeConfig) {
  var self = this;
  return function onResponseFinish() {
    // It's only at the response 'finish' event that we can be sure that req.route is set.
    // This is due to express populating it in the app.router middleware.
    if (!req.route) {
      return false; // If a non-route middleware has been run this would be the case. E.g. favicon.ico
    }

    // If a route is excluded in the configuration we end the middleware having made no requests
    // to [Keen.IO](http://keen.io).
    if (self.options.hasOwnProperty('excludeRoutes')) {
      if (self.routesHandler.isExcludedRoute(req.route)) {
        return false;
      }
    }

    // If a route has data stored in the 'routes' key of the configuration, we fetch this data.
    if (self.options.hasOwnProperty('routes')) {
      var eventCollectionMetadata = self.routesHandler.getRouteConfig(req.route);
       // If a route has no config but the configuration does have a routes key then consider it excluded.
      if (!eventCollectionMetadata) { return false; }

      routeConfig = helpers.extend({}, eventCollectionMetadata, routeConfig);
    }

    var identity = self.identifyHandler.identify(req),
        parsedRequestData = self.requestHandler.parseRequestObject(req),
        parsedResponseData = _parsedResponseData;

    var eventCollection, keenEvent;
    // We test to see if an event collection name is defined by any configuration and that it is valid.
    // If it is not valid then we attempt to generate it from the route.
    if (routeConfig.eventCollectionName) {
      if (self.eventCollectionHandler._isValidEventCollectionName(routeConfig.eventCollectionName)) {
        eventCollection = routeConfig.eventCollectionName;
      } else {
        self.emit('error', 'ERROR: ' + routeConfig.eventCollectionName + ' is not a valid event collection name so another has been automatically generated.');
        eventCollection = self.eventCollectionHandler.generateName(req.route);
      }
    } else {
      eventCollection = self.eventCollectionHandler.generateName(req.route);
    }

    keenEvent = self.keenEventHandler.generateEvent(identity, parsedRequestData, parsedResponseData, routeConfig);

    self._track(eventCollection, keenEvent);
  };
};

// Given an event collection name and a Keen.IO event we send out a track event
// and make a request to Keen.IO.
KeenioMiddleware.prototype._track = function (eventCollection, keenEvent) {
  var keenioMiddlewareScope = this;
  this.emit("track", { eventCollection: eventCollection, keenEvent: keenEvent });
  this.keenClient.addEvent(eventCollection, keenEvent, function (err, res) {
    if (err) {
      return keenioMiddlewareScope.emit("error", err);
    }

    if (res.created) {
      keenioMiddlewareScope.emit("info", "Keen.IO event created.");
    } else {
      keenioMiddlewareScope.emit("info", "Keen.IO event not created.");
    }
  });
};

KeenioMiddleware.prototype._configureHandlers = function (options) {
  this.routesHandler = new RoutesModule(options, this._ee);
  this.proxyResponseHandler = new ProxyResponseModule(options, this._ee);
  this.requestHandler = new RequestModule(options, this._ee);
  this.identifyHandler = new IdentifyModule(options, this._ee);
  this.eventCollectionHandler = new EventCollectionModule(options, this._ee);
  this.keenEventHandler = new KeenEventModule(options, this._ee);
};

exports = module.exports = new KeenioMiddleware();<commit_msg>Alias.<commit_after>"use strict";

// Grab dependencies required by the middleware.
var util         = require('util'),
    keen         = require('keen.io'),
    // See [connect-friendwares](http://github.com/sebinsua/connect-friendwares).
    friendwares  = require('connect-friendwares'),
    EventEmitter = require('events').EventEmitter;

// See [lib/core/helpers](http://sebinsua.github.io/express-keenio/helpers.html).
var helpers       = require('./core/helpers'),
    // See [lib/core/options-parser](http://sebinsua.github.io/express-keenio/options-parser.html).
    optionsParser = require('./core/options-parser');

    // See [lib/routes](http://sebinsua.github.io/express-keenio/routes.html).
var RoutesModule          = require('./routes'),
    // See [lib/parse/proxy-response](http://sebinsua.github.io/express-keenio/proxy-response.html).
    ProxyResponseModule   = require('./parse/proxy-response'),
    // See [lib/parse/request](http://sebinsua.github.io/express-keenio/request.html).
    RequestModule         = require('./parse/request'),
    // See [lib/parse/identify](http://sebinsua.github.io/express-keenio/identify.html).
    IdentifyModule        = require('./parse/identify'),
     // See [lib/event-collection](http://sebinsua.github.io/express-keenio/event-collection.html).
    EventCollectionModule = require('./event-collection'),
    // See [lib/keen-event](http://sebinsua.github.io/express-keenio/keen-event.html).
    KeenEventModule       = require('./keen-event');

// Construct the middleware uninitialised with some default events.
function KeenioMiddleware () {
  this.options = {};
  this.handlers = {};
  this.initialized = false;

  // This initialises an internal `_ee` EventEmitter with the events: error, info, track and flush.
  this._ee = helpers.setDefaultEvents(new EventEmitter(), ['error', 'info', 'track', 'flush']);
}

// Bind the EventEmitter methods to the middleware.
helpers.forEach(EventEmitter.prototype, function (fn, key) {
  KeenioMiddleware.prototype[key] = function () {
    this._ee[key].apply(this, arguments);
  };
});

// Before the middleware can be used it must have its configuration passed into it.
KeenioMiddleware.prototype.configure = function (options) {
  this.options = optionsParser.parse(options);

  this._configureHandlers(this.options);

  this.keenClient = keen.configure(options.client);
  this.initialized = true;
  this.emit('initialized');
};

// A method may be called to check whether the middleware has had configure() called before
// it is used. It will throw an exception if the middleware has not yet been initialised.
KeenioMiddleware.prototype.checkInitialized = function () {
  if (!this.initialized) {
    throw new Error('express-keenio middleware must be configured before use. Please call ' +
                    'keenio-middleware#configure(options).');
  }
  return true;
};

// This method allows us to check that the middleware is in use by Express and not
// just Connect. It does this by checking for the existence of a middleware called
// 'expressInit'.
//
// The purpose of this is that the middleware requires some behaviours given by Express,
// particularly relating to route definitions.
KeenioMiddleware.prototype.isMiddlewareUsedByExpress = function (app) {
  return friendwares(app).has('expressInit');
};

// This is exposed in order that an instance of the middleware can be passed directly into
// app.use() - which either executes a function, or looks for the function at an object's
// `handle` property.
KeenioMiddleware.prototype.handle = function keenioHandler(req, res, next) {
  var middleware = this.handleAll();
  middleware(req, res, next);
};

KeenioMiddleware.prototype.handleAll = KeenioMiddleware.prototype.track = function () {
  this.checkInitialized();

  return this._generateHandler({});
};

// `trackRoute()` allows the middlewares to be attached to specific [Express.js](http://expressjs.com)
// routes and to explicitly define their event collection name.
KeenioMiddleware.prototype.trackRoute = function (eventCollectionName, eventTag) {
  this.checkInitialized();

  return this._generateHandler({
    eventCollectionName: eventCollectionName,
    tag: eventTag
  });
};

// This is an internal method which generates the middleware handler when given a routeConfig.
// The routeConfig must be empty if we are using the middleware against the whole app.
KeenioMiddleware.prototype._generateHandler = function (routeConfig) {
  var self = this;

  return function keenioHandler(req, res, next) {
    // We ensure that the middleware is being used by an express app.
    if (!req.app || !self.isMiddlewareUsedByExpress(req.app)) {
      self.emit('error', 'Currently this middleware is only supported by Express.js.');
      return next();
    }

    // We swap the methods on a response object with proxied versions, and respond
    // with a parsedResponseData object that is still in scope of the res object's proxied
    // response methods (closures.)
    // This may not be the most elegant solution but it works.
    var parsedResponseData = self.proxyResponseHandler.proxyResponseObject(res);

    // We setup a function that will be called when a response is finished, and pass
    // in the request object, routeConfig, and parsedResponseData object.
    // The parsedResponseData object at this point contains no data, but since it is
    // still in the scope of the proxied response object it will contain response data
    // by the time the response has had its `finish` event emitted.
    res.on("finish", self._finishResponse(req, parsedResponseData, routeConfig));

    return next();
  };
};

KeenioMiddleware.prototype._finishResponse = function (req, _parsedResponseData, routeConfig) {
  var self = this;
  return function onResponseFinish() {
    // It's only at the response 'finish' event that we can be sure that req.route is set.
    // This is due to express populating it in the app.router middleware.
    if (!req.route) {
      return false; // If a non-route middleware has been run this would be the case. E.g. favicon.ico
    }

    // If a route is excluded in the configuration we end the middleware having made no requests
    // to [Keen.IO](http://keen.io).
    if (self.options.hasOwnProperty('excludeRoutes')) {
      if (self.routesHandler.isExcludedRoute(req.route)) {
        return false;
      }
    }

    // If a route has data stored in the 'routes' key of the configuration, we fetch this data.
    if (self.options.hasOwnProperty('routes')) {
      var eventCollectionMetadata = self.routesHandler.getRouteConfig(req.route);
       // If a route has no config but the configuration does have a routes key then consider it excluded.
      if (!eventCollectionMetadata) { return false; }

      routeConfig = helpers.extend({}, eventCollectionMetadata, routeConfig);
    }

    var identity = self.identifyHandler.identify(req),
        parsedRequestData = self.requestHandler.parseRequestObject(req),
        parsedResponseData = _parsedResponseData;

    var eventCollection, keenEvent;
    // We test to see if an event collection name is defined by any configuration and that it is valid.
    // If it is not valid then we attempt to generate it from the route.
    if (routeConfig.eventCollectionName) {
      if (self.eventCollectionHandler._isValidEventCollectionName(routeConfig.eventCollectionName)) {
        eventCollection = routeConfig.eventCollectionName;
      } else {
        self.emit('error', 'ERROR: ' + routeConfig.eventCollectionName + ' is not a valid event collection name so another has been automatically generated.');
        eventCollection = self.eventCollectionHandler.generateName(req.route);
      }
    } else {
      eventCollection = self.eventCollectionHandler.generateName(req.route);
    }

    keenEvent = self.keenEventHandler.generateEvent(identity, parsedRequestData, parsedResponseData, routeConfig);

    self._trackEvent(eventCollection, keenEvent);
  };
};

// Given an event collection name and a Keen.IO event we send out a track event
// and make a request to Keen.IO.
KeenioMiddleware.prototype._trackEvent = function (eventCollection, keenEvent) {
  var keenioMiddlewareScope = this;
  this.emit("track", { eventCollection: eventCollection, keenEvent: keenEvent });
  this.keenClient.addEvent(eventCollection, keenEvent, function (err, res) {
    if (err) {
      return keenioMiddlewareScope.emit("error", err);
    }

    if (res.created) {
      keenioMiddlewareScope.emit("info", "Keen.IO event created.");
    } else {
      keenioMiddlewareScope.emit("info", "Keen.IO event not created.");
    }
  });
};

KeenioMiddleware.prototype._configureHandlers = function (options) {
  this.routesHandler = new RoutesModule(options, this._ee);
  this.proxyResponseHandler = new ProxyResponseModule(options, this._ee);
  this.requestHandler = new RequestModule(options, this._ee);
  this.identifyHandler = new IdentifyModule(options, this._ee);
  this.eventCollectionHandler = new EventCollectionModule(options, this._ee);
  this.keenEventHandler = new KeenEventModule(options, this._ee);
};

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