<commit_before>import Board from '../../../../frontend/src/shogi/board';
import Piece from '../../../../frontend/src/shogi/piece';
import memo from 'memo-is';
import _ from 'lodash';

describe('#enhancePlaceablePoint', () => {
  describe('black', () => {
    context('the normal pattern', () => {
      const position = memo().is(() => {
        return (
          [
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['P', '*', '*'],
            ['*', '*', '*'],
          ]
        );
      });

      it('change property of piece that is placeable', () => {
        const board = new Board(position());
        const piece = Piece.create({ type: 'N', x: 0, y: 0 });

        board.enhancePlaceablePoint(piece);

        const placeablePieces = board.board.map((row) => {
          return (
            row.filter((cell) => { return(cell.isPlaced); })
          );
        });

        _.flattenDeep(placeablePieces).should.eql(
          [
            Piece.create({ type: '*', x: 8, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 7, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 7, y: 4, isPlaced: true}),
          ]
        );
      });
    });

    context.skip('if move piece, king is taken', () => {
    });
  });

  describe('white', () => {
    context('the normal pattern', () => {
      const position = memo().is(() => {
        return (
          [
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['b', '*'],
            ['*', '*'],
            ['*', '*'],
          ]
        );
      });

      it('change property of piece that is placeable', () => {
        const board = new Board(position());
        const piece = Piece.create({ type: 'n', x: 0, y: 0 });

        board.enhancePlaceablePoint(piece);

        const placeablePieces = board.board.map((row) => {
          return (
            row.filter((cell) => { return(cell.isPlaced); })
          );
        });

        _.flattenDeep(placeablePieces).should.eql(
          [
            Piece.create({ type: '*', x: 9, y: 1, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 1, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 2, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 2, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 5, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 5, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 6, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 6, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 7, isPlaced: true}),
          ]
        );
      });
    });

    context.skip('if move piece, king is taken', () => {
    });
  });
});
<commit_msg>Add movePiece test for Knight.<commit_after>import Board from '../../../../frontend/src/shogi/board';
import Piece from '../../../../frontend/src/shogi/piece';
import memo from 'memo-is';
import _ from 'lodash';

describe('#enhancePlaceablePoint', () => {
  describe('black', () => {
    context('the normal pattern', () => {
      const position = memo().is(() => {
        return (
          [
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['P', '*', '*'],
            ['*', '*', '*'],
          ]
        );
      });

      it('change property of piece that is placeable', () => {
        const board = new Board(position());
        const piece = Piece.create({ type: 'N', x: 0, y: 0 });

        board.enhancePlaceablePoint(piece);

        const placeablePieces = board.board.map((row) => {
          return (
            row.filter((cell) => { return(cell.isPlaced); })
          );
        });

        _.flattenDeep(placeablePieces).should.eql(
          [
            Piece.create({ type: '*', x: 8, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 7, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 7, y: 4, isPlaced: true}),
          ]
        );
      });
    });

    context.skip('if move piece, king is taken', () => {
    });
  });

  describe('white', () => {
    context('the normal pattern', () => {
      const position = memo().is(() => {
        return (
          [
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['*', '*'],
            ['b', '*'],
            ['*', '*'],
            ['*', '*'],
          ]
        );
      });

      it('change property of piece that is placeable', () => {
        const board = new Board(position());
        const piece = Piece.create({ type: 'n', x: 0, y: 0 });

        board.enhancePlaceablePoint(piece);

        const placeablePieces = board.board.map((row) => {
          return (
            row.filter((cell) => { return(cell.isPlaced); })
          );
        });

        _.flattenDeep(placeablePieces).should.eql(
          [
            Piece.create({ type: '*', x: 9, y: 1, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 1, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 2, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 2, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 3, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 4, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 5, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 5, isPlaced: true}),
            Piece.create({ type: '*', x: 9, y: 6, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 6, isPlaced: true}),
            Piece.create({ type: '*', x: 8, y: 7, isPlaced: true}),
          ]
        );
      });
    });

    context.skip('if move piece, king is taken', () => {
    });
  });
});

describe('#movePiece', () => {
  context('mismatch piece of first argument and piece in the board', () => {
    const position = memo().is(() => {
      return (
        [
          ['*', '*', '*'],
          ['*', 'n', '*'],
          ['*', '*', '*'],
          ['*', '*', '*'],
          ['*', 'N', '*'],
        ]
      );
    });

    it('throw exception', () => {
      const board = new Board(position());
      const fromPiece = Piece.create({type: 'N', x: 7, y: 5});
      const toPiece = Piece.create({type: '*', x: 9, y: 3});
      (() => board.movePiece(fromPiece, toPiece)).should.throw();
    });
  });

  context('mismatch piece of second argument and piece in the board', () => {
    const position = memo().is(() => {
      return (
        [
          ['*', '*', '*'],
          ['*', 'n', '*'],
          ['*', '*', '*'],
          ['*', '*', '*'],
          ['*', 'N', '*'],
        ]
      );
    });

    it('throw exception', () => {
      const board = new Board(position());
      const fromPiece = Piece.create({type: 'N', x: 8, y: 5});
      const toPiece = Piece.create({type: 'n', x: 9, y: 3});
      (() => board.movePiece(fromPiece, toPiece)).should.throw();
    });
  });

  describe('black', () => {
    const position = memo().is(() => {
      return (
        [
          ['*', '*', '*'],
          ['*', '*', '*'],
          ['*', '*', '*'],
          ['r', '*', '*'],
          ['*', 'N', '*'],
        ]
      );
    });

    context('toPiece is movable', () => {
      it('moves piece', () => {
        const board = new Board(position());
        const fromPiece = Piece.create({type: 'N', x: 8, y: 5});
        const toPiece = Piece.create({type: '*', x: 9, y: 3});

        board.movePiece(fromPiece, toPiece).toArray().should.eql(
          [
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['N', '*', '*'],
            ['r', '*', '*'],
            ['*', '*', '*'],
          ]
        );
      });
    });

    context('toPiece is not movable', () => {
      it('does not move piece', () => {
        const board = new Board(position());
        const fromPiece = Piece.create({type: 'N', x: 8, y: 5});
        const toPiece = Piece.create({type: '*', x: 7, y: 4});

        board.movePiece(fromPiece, toPiece).toArray().should.eql(
          [
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['r', '*', '*'],
            ['*', 'N', '*'],
          ]
        );
      });
    });
  });

  describe('white', () => {
    const position = memo().is(() => {
      return (
        [
          ['*', '*', '*'],
          ['*', '*', '*'],
          ['*', 'n', '*'],
          ['P', '*', '*'],
          ['*', '*', '*'],
        ]
      );
    });

    context('toPiece is movable', () => {
      it('moves piece', () => {
        const board = new Board(position());
        const fromPiece = Piece.create({type: 'n', x: 8, y: 3});
        const toPiece = Piece.create({type: '*', x: 9, y: 5});

        board.movePiece(fromPiece, toPiece).toArray().should.eql(
          [
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['P', '*', '*'],
            ['n', '*', '*'],
          ]
        );
      });
    });

    context('toPiece is not movable', () => {
      it('does not move piece', () => {
        const board = new Board(position());
        const fromPiece = Piece.create({type: 'n', x: 8, y: 3});
        const toPiece = Piece.create({type: '*', x: 7, y: 4});

        board.movePiece(fromPiece, toPiece).toArray().should.eql(
          [
            ['*', '*', '*'],
            ['*', '*', '*'],
            ['*', 'n', '*'],
            ['P', '*', '*'],
            ['*', '*', '*'],
          ]
        );
      });
    });
  });
});
<|endoftext|>


<commit_before>// Purpose: Update .editorconfig file according app/config.json

// Import modules
const env = require('./env')
const alert = require('./alert')
const fs = require('fs-extra')
const path = require('path')

// Check availability of the config
if (!env.cfg.editorConfig) throw new Error('Missing editorConfig item in config.json file.')

// Create file content
let content = ''

// Loop config options
for (let item in env.cfg.editorConfig) {
  content = content + item + (env.cfg.editorConfig[item] ? ' = ' + env.cfg.editorConfig[item] : '') + '\n'
}

// Create/update .editorconfig file
fs.writeFile(path.resolve(env.app, '.editorconfig'), content, (err) => {
  if (err) throw err
  alert('.editorconfig file updated.')
})
<commit_msg>eslint<commit_after>// Purpose: Update .editorconfig file according app/config.json

// Import modules
const env = require('./env')
const alert = require('./alert')
const fs = require('fs-extra')
const path = require('path')

// Check availability of the config
if (!env.cfg.editorConfig) {
  throw new Error('Missing editorConfig item in config.json file.')
}

// Create file content
let content = ''

// Loop config options
for (let item in env.cfg.editorConfig) {
  content = content + item + (env.cfg.editorConfig[item] ? ' = ' + env.cfg.editorConfig[item] : '') + '\n'
}

// Create/update .editorconfig file
fs.writeFile(path.resolve(env.app, '.editorconfig'), content, (err) => {
  if (err) throw err
  alert('.editorconfig file updated.')
})
<|endoftext|>


<commit_before>var Path = require('path')
var electron = require('electron')
var extend = require('xtend/mutable')

module.exports = function Window (config, path, opts) {
  var window = new electron.BrowserWindow(extend({
    show: false
  }, opts))

  electron.ipcMain.on('ready-to-show', handleReadyToShow)

  window.webContents.on('dom-ready', function () {
    window.webContents.executeJavaScript(`
      var electron = require('electron')
      var rootView = require(${JSON.stringify(path)})
      var h = require('mutant/h')

      electron.webFrame.setVisualZoomLevelLimits(1, 1)

      var config = ${JSON.stringify(config)}
      var data = ${JSON.stringify(opts.data)}
      var title = ${JSON.stringify(opts.title || 'Patchwork')}

      document.documentElement.querySelector('head').appendChild(
        h('title', title)
      )

      var shouldShow = ${opts.show !== false}
      var shouldConnect = ${opts.connect !== false}

      document.documentElement.replaceChild(h('body', [
        rootView(config, data)
      ]), document.body)
    `)
  })

  // setTimeout(function () {
  //   window.show()
  // }, 3000)

  window.webContents.on('will-navigate', function (e, url) {
    e.preventDefault()
    electron.shell.openExternal(url)
  })

  window.webContents.on('new-window', function (e, url) {
    e.preventDefault()
    electron.shell.openExternal(url)
  })

  window.on('closed', function () {
    electron.ipcMain.removeListener('ready-to-show', handleReadyToShow)
  })

  window.loadURL('file://' + Path.join(__dirname, '..', 'assets', 'base.html'))
  return window

  // scoped

  function handleReadyToShow (ev) {
    if (ev.sender === window) {
      window.show()
      electron.ipcMain.removeListener('ready-to-show', handleReadyToShow)
    }
  }
}
<commit_msg>Fix window frame regression<commit_after>var Path = require('path')
var electron = require('electron')
var extend = require('xtend/mutable')

module.exports = function Window (config, path, opts) {
  var window = new electron.BrowserWindow(extend({
    show: false,
    titleBarStyle: 'hidden',
  }, opts))

  electron.ipcMain.on('ready-to-show', handleReadyToShow)

  window.webContents.on('dom-ready', function () {
    window.webContents.executeJavaScript(`
      var electron = require('electron')
      var rootView = require(${JSON.stringify(path)})
      var h = require('mutant/h')

      electron.webFrame.setVisualZoomLevelLimits(1, 1)

      var config = ${JSON.stringify(config)}
      var data = ${JSON.stringify(opts.data)}
      var title = ${JSON.stringify(opts.title || 'Patchwork')}

      document.documentElement.querySelector('head').appendChild(
        h('title', title)
      )

      var shouldShow = ${opts.show !== false}
      var shouldConnect = ${opts.connect !== false}

      document.documentElement.replaceChild(h('body', [
        rootView(config, data)
      ]), document.body)
    `)
  })

  // setTimeout(function () {
  //   window.show()
  // }, 3000)

  window.webContents.on('will-navigate', function (e, url) {
    e.preventDefault()
    electron.shell.openExternal(url)
  })

  window.webContents.on('new-window', function (e, url) {
    e.preventDefault()
    electron.shell.openExternal(url)
  })

  window.on('closed', function () {
    electron.ipcMain.removeListener('ready-to-show', handleReadyToShow)
  })

  window.loadURL('file://' + Path.join(__dirname, '..', 'assets', 'base.html'))
  return window

  // scoped

  function handleReadyToShow (ev) {
    if (ev.sender === window) {
      window.show()
      electron.ipcMain.removeListener('ready-to-show', handleReadyToShow)
    }
  }
}
<|endoftext|>


<commit_before>/*! jquery.blurr <%= grunt.template.today("dd-mm-yyyy") % - Copyright 2014 Tom Hallam - MIT Licenced  >*/
!function(a,b,c,d){function e(e,h,i){this.$el=a(e),this.settings=a.extend({},g,h),this._defaults=g,this._name=f,this.cssPrefix=null,this.tpl='<svg><defs><filter id="blrIMG{{i}}"><feGaussianBlur id="filter_1" stdDeviation="{{sharpness}}" data-filterid="1"></feGaussianBlur><feComponentTransfer><feFuncR type="linear" slope="0.8"></feFuncR><feFuncG type="linear" slope="0.8"></feFuncG><feFuncB type="linear" slope="0.8"></feFuncB></feComponentTransfer></filter></defs><image x="{{offsetX}}" y="{{offsetY}}" width="100%" height="100%" xlink:href="{{href}}" filter="url(#blrIMG{{i}})" preserveAspectRatio="xMidYMid slice"></image></svg>',this.elementCount=i,this.supportsFilter=typeof SVGFEColorMatrixElement!==d&&2===SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE,this.supportsFilter=b.location.hash.length>0,_browserPrefixes=" -webkit- -moz- -o- -ms- ".split(" "),_cssPrefixString={},_cssPrefix=function(a){if(_cssPrefixString[a]||""===_cssPrefixString[a])return _cssPrefixString[a]+a;var b=c.createElement("div"),d=["","Moz","Webkit","O","ms","Khtml"];for(var e in d)if("undefined"!=typeof b.style[d[e]+a])return _cssPrefixString[a]=d[e],d[e]+a;return a.toLowerCase()},this.support={cssfilters:function(){var a=c.createElement("div");return a.style.cssText=_browserPrefixes.join("filter:blur(2px); "),!!a.style.length&&(c.documentMode===d||c.documentMode>9)}(),svgfilters:function(){var a=!1;try{a=typeof SVGFEColorMatrixElement!==d&&2==SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE}catch(b){}return a}()},this.cssPrefix=_cssPrefix("filter"),this.init()}var f="blurr",g={offsetX:0,offsetY:0,sharpness:40,height:300,callback:function(){}};a.extend(e.prototype,{init:function(){var a,b,c,d,e,f;return a=this.settings.href,b=this.settings.offsetX,c=this.settings.offsetY,d=this.settings.sharpness,e=this.settings.callback,f=this.settings.height,this.$el.data("image")&&(a=this.$el.data("image")),this.$el.data("href")&&(a=this.$el.data("href")),this.$el.data("offsetx")&&(b=this.$el.data("offsetx")),this.$el.data("offsety")&&(c=this.$el.data("offsety")),this.$el.data("sharpness")&&(d=this.$el.data("sharpness")),"undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=0),d="undefined"==typeof d||0===d.length||0>d||d>100?d>100?100:40:100-d,("undefined"==typeof f||0===d.length||0>d)&&(f=300),this.$el.addClass("has-blurr"),this.support.svgfilters&&!this.support.cssfilters?this.renderSVG(a,b,c,d,f,e):this.renderCSSFilter(a,b,c,d,f,e)},renderSVG:function(b,c,d,e,f,g){var h=this.tpl;h=h.replace("{{href}}",b),h=h.replace("{{offsetX}}",c),h=h.replace("{{offsetY}}",d),h=h.replace("{{sharpness}}",e),h=h.replace(/{{i}}/g,this.elementCount),a(h).appendTo(this.$el),this.$el.css({height:f,overflow:"hidden",background:"#000"}),this.$el.find("svg").css({"min-width":"110%","min-height":"110%","-webkit-transform":"translate3d(-50px, 0px, 75px) scale(1.25)",transform:"translate3d(-50px, 0px, 75px) scale(1.25)",position:"relative",right:0,left:0}),this.$el.find("div:first").css({position:"absolute",left:0,right:0,"z-index":100}),"function"==typeof g&&g.call(this,b,c,d,e)},renderCSSFilter:function(b,c,d,e,f){this.$el.css({height:f,overflow:"hidden",position:"relative",background:"#000"});var g;g=c&&d?c+"px "+d+"px":c&&!d?c+"px center":!c&&d?"center "+d+"px":"center center";{var h=a('

').css({background:"url("+b+")",left:0,right:0,top:-50,bottom:-50,width:this.$el.width(),"background-size":"150% auto","background-position":g,"-webkit-filter":"blur("+e+"px)","z-index":50,position:"absolute"}).prependTo(this.$el);this.cssPrefix}h.css(this.support.cssfilters?{prefix:"blur("+e+"px)"}:{prefix:'progid:DXImageTransform.Microsoft.Blur(PixelRadius="'+e+'")'}),this.$el.find("div").not(".blurr-bg").css({position:"absolute",left:0,right:0,"z-index":100})}}),a.fn[f]=function(b){return this.each(function(c){a.data(this,"plugin_"+f)||a.data(this,"plugin_"+f,new e(this,b,c))}),this}}(jQuery,window,document);<commit_msg>Corrected grunt error<commit_after>/*! jquery.blurr 01-08-2014 - Copyright 2014 Tom Hallam - MIT Licenced  >*/
!function(a,b,c,d){function e(e,h,i){this.$el=a(e),this.settings=a.extend({},g,h),this._defaults=g,this._name=f,this.cssPrefix=null,this.tpl='<svg><defs><filter id="blrIMG{{i}}"><feGaussianBlur id="filter_1" stdDeviation="{{sharpness}}" data-filterid="1"></feGaussianBlur><feComponentTransfer><feFuncR type="linear" slope="0.8"></feFuncR><feFuncG type="linear" slope="0.8"></feFuncG><feFuncB type="linear" slope="0.8"></feFuncB></feComponentTransfer></filter></defs><image x="{{offsetX}}" y="{{offsetY}}" width="100%" height="100%" xlink:href="{{href}}" filter="url(#blrIMG{{i}})" preserveAspectRatio="xMidYMid slice"></image></svg>',this.elementCount=i,this.supportsFilter=typeof SVGFEColorMatrixElement!==d&&2===SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE,this.supportsFilter=b.location.hash.length>0,_browserPrefixes=" -webkit- -moz- -o- -ms- ".split(" "),_cssPrefixString={},_cssPrefix=function(a){if(_cssPrefixString[a]||""===_cssPrefixString[a])return _cssPrefixString[a]+a;var b=c.createElement("div"),d=["","Moz","Webkit","O","ms","Khtml"];for(var e in d)if("undefined"!=typeof b.style[d[e]+a])return _cssPrefixString[a]=d[e],d[e]+a;return a.toLowerCase()},this.support={cssfilters:function(){var a=c.createElement("div");return a.style.cssText=_browserPrefixes.join("filter:blur(2px); "),!!a.style.length&&(c.documentMode===d||c.documentMode>9)}(),svgfilters:function(){var a=!1;try{a=typeof SVGFEColorMatrixElement!==d&&2==SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE}catch(b){}return a}()},this.cssPrefix=_cssPrefix("filter"),this.init()}var f="blurr",g={offsetX:0,offsetY:0,sharpness:40,height:300,callback:function(){}};a.extend(e.prototype,{init:function(){var a,b,c,d,e,f;return a=this.settings.href,b=this.settings.offsetX,c=this.settings.offsetY,d=this.settings.sharpness,e=this.settings.callback,f=this.settings.height,this.$el.data("image")&&(a=this.$el.data("image")),this.$el.data("href")&&(a=this.$el.data("href")),this.$el.data("offsetx")&&(b=this.$el.data("offsetx")),this.$el.data("offsety")&&(c=this.$el.data("offsety")),this.$el.data("sharpness")&&(d=this.$el.data("sharpness")),"undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=0),d="undefined"==typeof d||0===d.length||0>d||d>100?d>100?100:40:100-d,("undefined"==typeof f||0===d.length||0>d)&&(f=300),this.$el.addClass("has-blurr"),this.support.svgfilters&&!this.support.cssfilters?this.renderSVG(a,b,c,d,f,e):this.renderCSSFilter(a,b,c,d,f,e)},renderSVG:function(b,c,d,e,f,g){var h=this.tpl;h=h.replace("{{href}}",b),h=h.replace("{{offsetX}}",c),h=h.replace("{{offsetY}}",d),h=h.replace("{{sharpness}}",e),h=h.replace(/{{i}}/g,this.elementCount),a(h).appendTo(this.$el),this.$el.css({height:f,overflow:"hidden",background:"#000"}),this.$el.find("svg").css({"min-width":"110%","min-height":"110%","-webkit-transform":"translate3d(-50px, 0px, 75px) scale(1.25)",transform:"translate3d(-50px, 0px, 75px) scale(1.25)",position:"relative",right:0,left:0}),this.$el.find("div:first").css({position:"absolute",left:0,right:0,"z-index":100}),"function"==typeof g&&g.call(this,b,c,d,e)},renderCSSFilter:function(b,c,d,e,f){this.$el.css({height:f,overflow:"hidden",position:"relative",background:"#000"});var g;g=c&&d?c+"px "+d+"px":c&&!d?c+"px center":!c&&d?"center "+d+"px":"center center";{var h=a('

').css({background:"url("+b+")",left:0,right:0,top:-50,bottom:-50,width:this.$el.width(),"background-size":"150% auto","background-position":g,"-webkit-filter":"blur("+e+"px)","z-index":50,position:"absolute"}).prependTo(this.$el);this.cssPrefix}h.css(this.support.cssfilters?{prefix:"blur("+e+"px)"}:{prefix:'progid:DXImageTransform.Microsoft.Blur(PixelRadius="'+e+'")'}),this.$el.find("div").not(".blurr-bg").css({position:"absolute",left:0,right:0,"z-index":100})}}),a.fn[f]=function(b){return this.each(function(c){a.data(this,"plugin_"+f)||a.data(this,"plugin_"+f,new e(this,b,c))}),this}}(jQuery,window,document);<|endoftext|>


<commit_before>
/*!
 * Cluster - Worker
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter
  , spawn = require('child_process').spawn
  , binding = process.binding('net')
  , utils = require('./utils')
  , net = require('net');

// COMPAT:

net.Socket = net.Stream;

/**
 * Node binary.
 */

var node = process.execPath;

/**
 * Initialize a new `Worker` with the given `master`.
 *
 * Signals:
 *
 *   - `SIGINT`   immediately exit
 *   - `SIGTERM`  immediately exit
 *   - `SIGQUIT`  graceful exit
 *
 * @param {Master} master
 * @api private
 */

var Worker = module.exports = function Worker(master) {
  this.master = master;
  this.server = master.server;
  this.uid = Date.now() + Math.random();
};

/**
 * Inherit from `EventEmitter.prototype`.
 */

Worker.prototype.__proto__ = EventEmitter.prototype;

/**
 * Worker is a receiver.
 */

require('./mixins/receiver')(Worker.prototype);

/**
 * Start worker.
 *
 * @api private
 */

Worker.prototype.start = function(){
  var self = this
    , call = this.master.call;

  // proxy to provide worker id
  this.master.call = function(){
    var args = utils.toArray(arguments);
    // Allow calling master methods that don't take worker as first argument
    if (args[0] !== false) {
      args.unshift(self.id);
    }
    return call.apply(this, args);
  };

  // stdin
  this.stdin = new net.Socket(0, 'unix');
  this.stdin.setEncoding('ascii');
  this.stdin.on('data', function(chunk){ self.frame(chunk, self.uid); });
  this.stdin.resume();

  // demote usr/group
  if (this.server) {
    this.server.on('listening', function(){
      var group = self.options.group;
      if (group) process.setgid(group);
      var user = self.options.user;
      if (user) process.setuid(user);
    });

    // stdin
    this.stdin.on('fd', this.server.listenFD.bind(this.server));
  }

  // signal handlers
  process.on('SIGINT', this.destroy.bind(this));
  process.on('SIGTERM', this.destroy.bind(this));
  process.on('SIGQUIT', this.close.bind(this));

  // conditionally handle uncaughtException
  if (!process.listeners('uncaughtException').length) {
    process.on('uncaughtException', function(err){
      // stderr for logs
      console.error(err.stack || err.message);

      // report exception
      self.master.call('workerException', err);

      // exit
      process.nextTick(function(){
        self.destroy();
      });
    });
  }
};

/**
 * Received connect event, set the worker `id`
 * and `options`.
 *
 * @param {String} id
 * @param {Object} options
 * @api private
 */

Worker.prototype.connect = function(id, options){
  this.options = options;

  // worker id
  this.id = id;

  // timeout
  this.timeout = options.timeout;

  // title
  process.title = options['worker title'].replace('{n}', id);

  // notify master of connection
  this.master.call('connect');
};

/**
 * Immediate shutdown.
 *
 * @api private
 */

Worker.prototype.destroy = function(){
  process.exit();
};

/**
 * Perform graceful shutdown.
 *
 * @api private
 */

Worker.prototype.close = function(){
  var self = this
    , server = this.server;

  if (server && server.connections) {
    // stop accepting
    server.watcher.stop();

    // check pending connections
    setInterval(function(){
      self.master.call('workerWaiting', server.connections);
      server.connections || self.destroy();
    }, 2000);

    // timeout
    if (this.timeout) {
      setTimeout(function(){
        self.master.call('workerTimeout', self.timeout);
        self.destroy();
      }, this.timeout);
    }
  } else {
    this.destroy();
  }
};

/**
 * Spawn the worker with the given `id`.
 *
 * @param {Number} id
 * @return {Worker} for chaining
 * @api private
 */

Worker.prototype.spawn = function(id){
  var fds = binding.socketpair()
    , customFds = [fds[0]].concat(this.master.customFds)
    , env = {};

  // merge env
  for (var key in process.env) {
    env[key] = process.env[key];
  }

  this.id = env.CLUSTER_WORKER = id;

  // spawn worker process
  this.proc = spawn(
      node
    , this.master.cmd
    , { customFds: customFds, env: env });

  // unix domain socket for ICP + fd passing
  this.sock = new net.Socket(fds[1], 'unix');

  // saving file descriptors for later use
  this.fds = fds;

  return this;
};

/**
 * Invoke worker's `method` (called from Master).
 *
 * @param {String} method
 * @param {...} args
 * @api private
 */

Worker.prototype.call = function(method){
  this.sock.write(utils.frame({
      args: utils.toArray(arguments, 1)
    , method: method
  }), 'ascii');
};
<commit_msg>tweak previous commit slightly<commit_after>
/*!
 * Cluster - Worker
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter
  , spawn = require('child_process').spawn
  , binding = process.binding('net')
  , utils = require('./utils')
  , net = require('net');

// COMPAT:

net.Socket = net.Stream;

/**
 * Node binary.
 */

var node = process.execPath;

/**
 * Initialize a new `Worker` with the given `master`.
 *
 * Signals:
 *
 *   - `SIGINT`   immediately exit
 *   - `SIGTERM`  immediately exit
 *   - `SIGQUIT`  graceful exit
 *
 * @param {Master} master
 * @api private
 */

var Worker = module.exports = function Worker(master) {
  this.master = master;
  this.server = master.server;
  this.uid = Date.now() + Math.random();
};

/**
 * Inherit from `EventEmitter.prototype`.
 */

Worker.prototype.__proto__ = EventEmitter.prototype;

/**
 * Worker is a receiver.
 */

require('./mixins/receiver')(Worker.prototype);

/**
 * Start worker.
 *
 * @api private
 */

Worker.prototype.start = function(){
  var self = this
    , call = this.master.call;

  // proxy to provide worker id
  this.master.call = function(){
    var args = utils.toArray(arguments);
    // Allow calling master methods that
    // don't take worker as first argument
    if (false !== args[0]) args.unshift(self.id);
    return call.apply(this, args);
  };

  // stdin
  this.stdin = new net.Socket(0, 'unix');
  this.stdin.setEncoding('ascii');
  this.stdin.on('data', function(chunk){ self.frame(chunk, self.uid); });
  this.stdin.resume();

  // demote usr/group
  if (this.server) {
    this.server.on('listening', function(){
      var group = self.options.group;
      if (group) process.setgid(group);
      var user = self.options.user;
      if (user) process.setuid(user);
    });

    // stdin
    this.stdin.on('fd', this.server.listenFD.bind(this.server));
  }

  // signal handlers
  process.on('SIGINT', this.destroy.bind(this));
  process.on('SIGTERM', this.destroy.bind(this));
  process.on('SIGQUIT', this.close.bind(this));

  // conditionally handle uncaughtException
  if (!process.listeners('uncaughtException').length) {
    process.on('uncaughtException', function(err){
      // stderr for logs
      console.error(err.stack || err.message);

      // report exception
      self.master.call('workerException', err);

      // exit
      process.nextTick(function(){
        self.destroy();
      });
    });
  }
};

/**
 * Received connect event, set the worker `id`
 * and `options`.
 *
 * @param {String} id
 * @param {Object} options
 * @api private
 */

Worker.prototype.connect = function(id, options){
  this.options = options;

  // worker id
  this.id = id;

  // timeout
  this.timeout = options.timeout;

  // title
  process.title = options['worker title'].replace('{n}', id);

  // notify master of connection
  this.master.call('connect');
};

/**
 * Immediate shutdown.
 *
 * @api private
 */

Worker.prototype.destroy = function(){
  process.exit();
};

/**
 * Perform graceful shutdown.
 *
 * @api private
 */

Worker.prototype.close = function(){
  var self = this
    , server = this.server;

  if (server && server.connections) {
    // stop accepting
    server.watcher.stop();

    // check pending connections
    setInterval(function(){
      self.master.call('workerWaiting', server.connections);
      server.connections || self.destroy();
    }, 2000);

    // timeout
    if (this.timeout) {
      setTimeout(function(){
        self.master.call('workerTimeout', self.timeout);
        self.destroy();
      }, this.timeout);
    }
  } else {
    this.destroy();
  }
};

/**
 * Spawn the worker with the given `id`.
 *
 * @param {Number} id
 * @return {Worker} for chaining
 * @api private
 */

Worker.prototype.spawn = function(id){
  var fds = binding.socketpair()
    , customFds = [fds[0]].concat(this.master.customFds)
    , env = {};

  // merge env
  for (var key in process.env) {
    env[key] = process.env[key];
  }

  this.id = env.CLUSTER_WORKER = id;

  // spawn worker process
  this.proc = spawn(
      node
    , this.master.cmd
    , { customFds: customFds, env: env });

  // unix domain socket for ICP + fd passing
  this.sock = new net.Socket(fds[1], 'unix');

  // saving file descriptors for later use
  this.fds = fds;

  return this;
};

/**
 * Invoke worker's `method` (called from Master).
 *
 * @param {String} method
 * @param {...} args
 * @api private
 */

Worker.prototype.call = function(method){
  this.sock.write(utils.frame({
      args: utils.toArray(arguments, 1)
    , method: method
  }), 'ascii');
};
<|endoftext|>


<commit_before>var os = require("os");
var util = require("util");
var exec = require('child_process').exec;
var domain = require('domain');
var EventEmitter = require('events').EventEmitter;
var connection   = require(__dirname + "/connection.js").connection;
var queue        = require(__dirname + "/queue.js").queue;
var pluginRunner = require(__dirname + "/pluginRunner.js");

var worker = function(options, jobs){
  var self = this;
  if(!jobs){ jobs = {}; }

  var defaults = self.defaults();
  for(var i in defaults){
    if(options[i] === undefined || options[i] === null){
      options[i] = defaults[i];
    }
  }

  self.options = options;
  self.jobs = prepareJobs(jobs);
  self.name = self.options.name;
  self.queues = self.options.queues;
  self.error = null;
  self.ready = false;
  self.running = false;
  self.working = false;
  self.job = null;

  self.queueObject = new queue({connection: options.connection}, self.jobs);

  self.queueObject.on('error', function(err){
    self.emit('error', err);
  });
};

util.inherits(worker, EventEmitter);

worker.prototype.defaults = function(){
  var self = this;
  return {
    name:      os.hostname() + ":" + process.pid, // assumes only one worker per node process
    queues:    "*",
    timeout:   5000,
    looping:   true,
  };
};

worker.prototype.connect = function(callback){
  var self = this;
  self.queueObject.connect(function(){
    self.connection = self.queueObject.connection;
    self.checkQueues(function(){
      if(typeof callback === 'function'){ callback(); }
    });
  });
};

worker.prototype.start = function() {
  var self = this;
  if(self.ready){
    self.emit('start');
    self.init(function(){
      self.poll();
    });
  }
};

worker.prototype.end = function(callback) {
  var self = this;
  self.running = false;
  if (self.working === true){
    setTimeout(function(){
      self.end(callback);
    }, self.options.timeout);
  }else{
    self.untrack(self.name, self.stringQueues(), function(){
      self.queueObject.end(function() {
        self.emit('end');
        if(typeof callback === 'function'){ callback(); }
      });
    });
  }
};

worker.prototype.poll = function(nQueue, callback) {
  var self = this;
  if (nQueue === null || nQueue === undefined) {
    nQueue = 0;
  }
  if (!self.running) {
    if(typeof callback === 'function'){ callback(); }
  }else{
    self.queue = self.queues[nQueue];
    self.emit('poll', self.queue);
    if(self.queue === null || self.queue === undefined){
      self.checkQueues(function(){
        self.pause();
      });
    }else if(self.working === true){
      var err = new Error('refusing to get new job, already working');
      self.emit('error', self.queue, null, err);
    }else{
      self.working = true;
      self.connection.redis.lpop(self.connection.key('queue', self.queue), function(err, resp){
        if(!err && resp){
          var currentJob = JSON.parse(resp.toString());
          if(self.options.looping){
            self.perform(currentJob);
          }else{
            callback(currentJob);
          }
        }else{
          if(err){
            self.emit('error', self.queue, null, err);
          }
          self.working = false;
          if(nQueue === self.queues.length - 1){
            process.nextTick(function() {
              if(self.options.looping){
                self.pause();
              }else{
                callback();
              }
            });
          }else{
            process.nextTick(function(){
              self.poll(nQueue + 1, callback);
            });
          }
        }
      });
    }
  }
};

worker.prototype.perform = function(job, callback) {
  var self = this;
  var d = domain.create();
  self.job = job;
  d.on('error', function(err){
    self.error = err;
    self.completeJob(null, true, callback);
  });
  d.run(function(){
    self.error = null;
    if (!self.jobs[job["class"]]){
      self.error = new Error("No job defined for class '"+job["class"]+"'");
      self.completeJob(null, true, callback);
    }else{
      var cb = self.jobs[job["class"]].perform;
      self.emit('job', self.queue, job);
      if(cb) {
        var returnCounter = 0; // a state counter to prevent multiple returns from poor jobs or plugins
        var callbackError = new Error('refusing to continue with job, multiple callbacks detected');
        pluginRunner.runPlugins(self, 'before_perform', job["class"], self.queue, self.jobs[job["class"]], job.args, function(err, toRun){
          returnCounter++;
          if(returnCounter !== 1){
            self.emit('failure', self.queue, job, callbackError);
          }else if(toRun === false){
            self.completeJob(null, false, callback);
          }else{
            self.error = err;
            self.workingOn(job);
            var args;
            if(job.args === undefined || (job.args instanceof Array) === true){
              args = job.args;
            }else{
              args = [job.args];
            }

            var combinedInputs = [].slice.call(args).concat([function(err, result){
              returnCounter++;
              if(returnCounter !== 2){
                self.emit('failure', self.queue, job, callbackError);
              }else{
                self.error = err;
                pluginRunner.runPlugins(self, 'after_perform', job["class"], self.queue, self.jobs[job["class"]], job.args, function(e, toRun){
                  if(self.error === undefined && e){ self.error = e; }
                  returnCounter++;
                  if(returnCounter !== 3){
                    self.emit('failure', self.queue, job, callbackError);
                  }else{
                    self.completeJob(result, true, callback);
                  }
                });
              }
            }]);

            // When returning the payload back to redis (on error), it is important to make it immutable
            // https://github.com/taskrabbit/node-resque/issues/99
            // Note: if an input is a string or a number, you CANNOT freeze it saddly.
            for(var i in combinedInputs){ Object.freeze(combinedInputs[i]); }

            cb.apply(self, combinedInputs);
          }
        });
      }else{
        self.error = new Error("Missing Job: " + job["class"]);
        self.completeJob(null, true, callback);
      }
    }
  });
};

worker.prototype.completeJob = function(result, toRespond, callback){
  var self = this;
  var job = self.job;
  if(self.error){
    self.fail(self.error, job);
  }else if(toRespond){
    self.succeed(result, job);
  }
  self.doneWorking();
  self.job = null;
  process.nextTick((function() {
    if(self.options.looping){
      return self.poll();
    }else{
      callback();
    }
  }));
};

worker.prototype.succeed = function(result, job) {
  var self = this;
  self.connection.redis.incr(self.connection.key('stat', 'processed'));
  self.connection.redis.incr(self.connection.key('stat', 'processed', self.name));
  self.emit('success', self.queue, job, result);
};

worker.prototype.fail = function(err, job) {
  var self = this;
  self.connection.redis.incr(self.connection.key('stat', 'failed'));
  self.connection.redis.incr(self.connection.key('stat', 'failed', self.name));
  self.connection.redis.rpush(self.connection.key('failed'), JSON.stringify(self.failurePayload(err, job)));
  self.emit('failure', self.queue, job, err);
};

worker.prototype.pause = function() {
  var self = this;
  self.emit('pause');
  setTimeout(function() {
    if (!self.running) {
      return;
    }
    self.poll();
  }, self.options.timeout);
};

worker.prototype.workingOn = function(job) {
  var self = this;
  self.connection.redis.set(self.connection.key('worker', self.name, self.stringQueues()), JSON.stringify({
    run_at: (new Date()).toString(),
    queue: self.queue,
    payload: job,
    worker: self.name,
  }));
};

worker.prototype.doneWorking = function() {
  var self = this;
  self.working = false;
  self.connection.redis.del(self.connection.key('worker', self.name, self.stringQueues()));
};

worker.prototype.track = function(callback) {
  var self = this;
  self.running = true;
  self.connection.redis.sadd(self.connection.key('workers'), (self.name + ":" + self.stringQueues()), function(){
    if(typeof callback === 'function'){ callback(); }
  });
};

worker.prototype.untrack = function(name, queues, callback) {
  var self = this;
  if(self.connection && self.connection.redis){
    self.connection.redis.srem(self.connection.key('workers'), (name + ":" + queues), function(){
      self.connection.redis.del([
        self.connection.key('worker', name, self.stringQueues()),
        self.connection.key('worker', name, self.stringQueues(), 'started'),
        self.connection.key('stat', 'failed', name),
        self.connection.key('stat', 'processed', name)
      ], function(err){
        if(typeof callback === 'function'){ callback(err); }
      });
    });
  }else{
    callback();
  }
};

worker.prototype.init = function(callback) {
  var self = this;
  var args, _ref;
  self.track();
  self.connection.redis.set(self.connection.key('worker', self.name, self.stringQueues(), 'started'), (new Date()).toString(), function(){
    if(typeof callback === 'function'){ callback(); }
  });
};

worker.prototype.workerCleanup = function(callback){
  var self = this;
  self.getPids(function(err, pids){
    self.connection.redis.smembers(self.connection.key('workers'), function(err, workers){
      if(err){
        if(typeof callback === 'function'){ callback(err); }
        else{ self.emit('error', null, null, err); }
        return;
      }
      workers.forEach(function(w){
        var parts = w.split(":");
        var host = parts[0]; var pid = parseInt(parts[1]); var queues = parseInt(parts[2]);
        if(host === os.hostname() && pids.indexOf(pid) < 0){
          (function(w){
            self.emit("cleaning_worker", w, pid);
            var parts = w.split(":");
            var queues = parts.splice(-1, 1);
            var pureName = parts.join(':');
            self.untrack(pureName, queues);
          })(w);
        }
      });
      if(typeof callback === 'function'){ callback(); }
    });
  });
};

worker.prototype.getPids = function(callback){
  var cmd = '';
  if ( process.platform === 'win32' ) {
    cmd = 'for /f "usebackq tokens=2 skip=2" %i in (`tasklist /nh`) do @echo %i';
  } else {
    cmd = 'ps awx | grep -v grep';
  }

  var child = exec(cmd, function(error, stdout, stderr){
    var pids = [];
    stdout.split("\n").forEach(function(line){
      line = line.trim();
      if(line.length > 0){
        var pid = parseInt(line.split(' ')[0]);
        pids.push(pid);
      }
    });
    callback(error, pids);
  });
};

worker.prototype.checkQueues = function(callback){
  var self = this;
  if (typeof self.queues == "string"){
    self.queues = [self.queues];
  }
  if (self.ready === true && self.queues.length > 0 && self.queues.shift) {
    return;
  }
  if (( self.queues[0] === '*' && self.queues.length == 1 ) || self.queues.length === 0 ) {
    self.originalQueue = "*";
    self.untrack(self.name, self.stringQueues(), function(){
      self.connection.redis.smembers(self.connection.key('queues'), function(err, resp) {
        self.queues = resp ? resp.sort() : [];
        self.track(function(){
          self.ready = true;
          if(typeof callback === 'function'){ callback(); }
        });
      });
    });
  }else{
    if(self.queues instanceof String){ self.queues = self.queues.split(','); }
    self.ready = true;
    if(typeof callback === 'function'){ callback(); }
  }
};

worker.prototype.failurePayload = function(err, job) {
  var self = this;
  return {
    worker: self.name,
    queue: self.queue,
    payload: job,
    exception: err.name,
    error: err.message,
    backtrace: err.stack ? err.stack.split('\n').slice(1) : null,
    failed_at: (new Date()).toString()
  };
};

worker.prototype.stringQueues = function(){
  var self = this;
  if(self.queues.length === 0){
    return ["*"].join(",");
  }else{
    try{
      return self.queues.join(',');
    }catch(e){
      return '';
    }
  }
};

function prepareJobs(jobs) {
  return Object.keys(jobs).reduce(function(h, k) {
    var job = jobs[k];
    h[k] = typeof job === 'function' ? { perform: job } : job;
    return h;
  }, {});
}

exports.worker = worker;
<commit_msg>more notes<commit_after>var os = require("os");
var util = require("util");
var exec = require('child_process').exec;
var domain = require('domain');
var EventEmitter = require('events').EventEmitter;
var connection   = require(__dirname + "/connection.js").connection;
var queue        = require(__dirname + "/queue.js").queue;
var pluginRunner = require(__dirname + "/pluginRunner.js");

var worker = function(options, jobs){
  var self = this;
  if(!jobs){ jobs = {}; }

  var defaults = self.defaults();
  for(var i in defaults){
    if(options[i] === undefined || options[i] === null){
      options[i] = defaults[i];
    }
  }

  self.options = options;
  self.jobs = prepareJobs(jobs);
  self.name = self.options.name;
  self.queues = self.options.queues;
  self.error = null;
  self.ready = false;
  self.running = false;
  self.working = false;
  self.job = null;

  self.queueObject = new queue({connection: options.connection}, self.jobs);

  self.queueObject.on('error', function(err){
    self.emit('error', err);
  });
};

util.inherits(worker, EventEmitter);

worker.prototype.defaults = function(){
  var self = this;
  return {
    name:      os.hostname() + ":" + process.pid, // assumes only one worker per node process
    queues:    "*",
    timeout:   5000,
    looping:   true,
  };
};

worker.prototype.connect = function(callback){
  var self = this;
  self.queueObject.connect(function(){
    self.connection = self.queueObject.connection;
    self.checkQueues(function(){
      if(typeof callback === 'function'){ callback(); }
    });
  });
};

worker.prototype.start = function() {
  var self = this;
  if(self.ready){
    self.emit('start');
    self.init(function(){
      self.poll();
    });
  }
};

worker.prototype.end = function(callback) {
  var self = this;
  self.running = false;
  if (self.working === true){
    setTimeout(function(){
      self.end(callback);
    }, self.options.timeout);
  }else{
    self.untrack(self.name, self.stringQueues(), function(){
      self.queueObject.end(function() {
        self.emit('end');
        if(typeof callback === 'function'){ callback(); }
      });
    });
  }
};

worker.prototype.poll = function(nQueue, callback) {
  var self = this;
  if (nQueue === null || nQueue === undefined) {
    nQueue = 0;
  }
  if (!self.running) {
    if(typeof callback === 'function'){ callback(); }
  }else{
    self.queue = self.queues[nQueue];
    self.emit('poll', self.queue);
    if(self.queue === null || self.queue === undefined){
      self.checkQueues(function(){
        self.pause();
      });
    }else if(self.working === true){
      var err = new Error('refusing to get new job, already working');
      self.emit('error', self.queue, null, err);
    }else{
      self.working = true;
      self.connection.redis.lpop(self.connection.key('queue', self.queue), function(err, resp){
        if(!err && resp){
          var currentJob = JSON.parse(resp.toString());
          if(self.options.looping){
            self.perform(currentJob);
          }else{
            callback(currentJob);
          }
        }else{
          if(err){
            self.emit('error', self.queue, null, err);
          }
          self.working = false;
          if(nQueue === self.queues.length - 1){
            process.nextTick(function() {
              if(self.options.looping){
                self.pause();
              }else{
                callback();
              }
            });
          }else{
            process.nextTick(function(){
              self.poll(nQueue + 1, callback);
            });
          }
        }
      });
    }
  }
};

worker.prototype.perform = function(job, callback) {
  var self = this;
  var d = domain.create();
  self.job = job;
  d.on('error', function(err){
    self.error = err;
    self.completeJob(null, true, callback);
  });
  d.run(function(){
    self.error = null;
    if (!self.jobs[job["class"]]){
      self.error = new Error("No job defined for class '"+job["class"]+"'");
      self.completeJob(null, true, callback);
    }else{
      var cb = self.jobs[job["class"]].perform;
      self.emit('job', self.queue, job);
      if(cb) {
        var returnCounter = 0; // a state counter to prevent multiple returns from poor jobs or plugins
        var callbackError = new Error('refusing to continue with job, multiple callbacks detected');
        pluginRunner.runPlugins(self, 'before_perform', job["class"], self.queue, self.jobs[job["class"]], job.args, function(err, toRun){
          returnCounter++;
          if(returnCounter !== 1){
            self.emit('failure', self.queue, job, callbackError);
          }else if(toRun === false){
            self.completeJob(null, false, callback);
          }else{
            self.error = err;
            self.workingOn(job);
            var args;
            if(job.args === undefined || (job.args instanceof Array) === true){
              args = job.args;
            }else{
              args = [job.args];
            }

            var combinedInputs = [].slice.call(args).concat([function(err, result){
              returnCounter++;
              if(returnCounter !== 2){
                self.emit('failure', self.queue, job, callbackError);
              }else{
                self.error = err;
                pluginRunner.runPlugins(self, 'after_perform', job["class"], self.queue, self.jobs[job["class"]], job.args, function(e, toRun){
                  if(self.error === undefined && e){ self.error = e; }
                  returnCounter++;
                  if(returnCounter !== 3){
                    self.emit('failure', self.queue, job, callbackError);
                  }else{
                    self.completeJob(result, true, callback);
                  }
                });
              }
            }]);

            // When returning the payload back to redis (on error), it is important that the orignal payload is preserved
            // To help with this, we can stry to make the inputs to the job immutible
            // https://github.com/taskrabbit/node-resque/issues/99
            // Note: if an input is a string or a number, you CANNOT freeze it saddly.
            for(var i in combinedInputs){ Object.freeze(combinedInputs[i]); }

            cb.apply(self, combinedInputs);
          }
        });
      }else{
        self.error = new Error("Missing Job: " + job["class"]);
        self.completeJob(null, true, callback);
      }
    }
  });
};

worker.prototype.completeJob = function(result, toRespond, callback){
  var self = this;
  var job = self.job;
  if(self.error){
    self.fail(self.error, job);
  }else if(toRespond){
    self.succeed(result, job);
  }
  self.doneWorking();
  self.job = null;
  process.nextTick((function() {
    if(self.options.looping){
      return self.poll();
    }else{
      callback();
    }
  }));
};

worker.prototype.succeed = function(result, job) {
  var self = this;
  self.connection.redis.incr(self.connection.key('stat', 'processed'));
  self.connection.redis.incr(self.connection.key('stat', 'processed', self.name));
  self.emit('success', self.queue, job, result);
};

worker.prototype.fail = function(err, job) {
  var self = this;
  self.connection.redis.incr(self.connection.key('stat', 'failed'));
  self.connection.redis.incr(self.connection.key('stat', 'failed', self.name));
  self.connection.redis.rpush(self.connection.key('failed'), JSON.stringify(self.failurePayload(err, job)));
  self.emit('failure', self.queue, job, err);
};

worker.prototype.pause = function() {
  var self = this;
  self.emit('pause');
  setTimeout(function() {
    if (!self.running) {
      return;
    }
    self.poll();
  }, self.options.timeout);
};

worker.prototype.workingOn = function(job) {
  var self = this;
  self.connection.redis.set(self.connection.key('worker', self.name, self.stringQueues()), JSON.stringify({
    run_at: (new Date()).toString(),
    queue: self.queue,
    payload: job,
    worker: self.name,
  }));
};

worker.prototype.doneWorking = function() {
  var self = this;
  self.working = false;
  self.connection.redis.del(self.connection.key('worker', self.name, self.stringQueues()));
};

worker.prototype.track = function(callback) {
  var self = this;
  self.running = true;
  self.connection.redis.sadd(self.connection.key('workers'), (self.name + ":" + self.stringQueues()), function(){
    if(typeof callback === 'function'){ callback(); }
  });
};

worker.prototype.untrack = function(name, queues, callback) {
  var self = this;
  if(self.connection && self.connection.redis){
    self.connection.redis.srem(self.connection.key('workers'), (name + ":" + queues), function(){
      self.connection.redis.del([
        self.connection.key('worker', name, self.stringQueues()),
        self.connection.key('worker', name, self.stringQueues(), 'started'),
        self.connection.key('stat', 'failed', name),
        self.connection.key('stat', 'processed', name)
      ], function(err){
        if(typeof callback === 'function'){ callback(err); }
      });
    });
  }else{
    callback();
  }
};

worker.prototype.init = function(callback) {
  var self = this;
  var args, _ref;
  self.track();
  self.connection.redis.set(self.connection.key('worker', self.name, self.stringQueues(), 'started'), (new Date()).toString(), function(){
    if(typeof callback === 'function'){ callback(); }
  });
};

worker.prototype.workerCleanup = function(callback){
  var self = this;
  self.getPids(function(err, pids){
    self.connection.redis.smembers(self.connection.key('workers'), function(err, workers){
      if(err){
        if(typeof callback === 'function'){ callback(err); }
        else{ self.emit('error', null, null, err); }
        return;
      }
      workers.forEach(function(w){
        var parts = w.split(":");
        var host = parts[0]; var pid = parseInt(parts[1]); var queues = parseInt(parts[2]);
        if(host === os.hostname() && pids.indexOf(pid) < 0){
          (function(w){
            self.emit("cleaning_worker", w, pid);
            var parts = w.split(":");
            var queues = parts.splice(-1, 1);
            var pureName = parts.join(':');
            self.untrack(pureName, queues);
          })(w);
        }
      });
      if(typeof callback === 'function'){ callback(); }
    });
  });
};

worker.prototype.getPids = function(callback){
  var cmd = '';
  if ( process.platform === 'win32' ) {
    cmd = 'for /f "usebackq tokens=2 skip=2" %i in (`tasklist /nh`) do @echo %i';
  } else {
    cmd = 'ps awx | grep -v grep';
  }

  var child = exec(cmd, function(error, stdout, stderr){
    var pids = [];
    stdout.split("\n").forEach(function(line){
      line = line.trim();
      if(line.length > 0){
        var pid = parseInt(line.split(' ')[0]);
        pids.push(pid);
      }
    });
    callback(error, pids);
  });
};

worker.prototype.checkQueues = function(callback){
  var self = this;
  if (typeof self.queues == "string"){
    self.queues = [self.queues];
  }
  if (self.ready === true && self.queues.length > 0 && self.queues.shift) {
    return;
  }
  if (( self.queues[0] === '*' && self.queues.length == 1 ) || self.queues.length === 0 ) {
    self.originalQueue = "*";
    self.untrack(self.name, self.stringQueues(), function(){
      self.connection.redis.smembers(self.connection.key('queues'), function(err, resp) {
        self.queues = resp ? resp.sort() : [];
        self.track(function(){
          self.ready = true;
          if(typeof callback === 'function'){ callback(); }
        });
      });
    });
  }else{
    if(self.queues instanceof String){ self.queues = self.queues.split(','); }
    self.ready = true;
    if(typeof callback === 'function'){ callback(); }
  }
};

worker.prototype.failurePayload = function(err, job) {
  var self = this;
  return {
    worker: self.name,
    queue: self.queue,
    payload: job,
    exception: err.name,
    error: err.message,
    backtrace: err.stack ? err.stack.split('\n').slice(1) : null,
    failed_at: (new Date()).toString()
  };
};

worker.prototype.stringQueues = function(){
  var self = this;
  if(self.queues.length === 0){
    return ["*"].join(",");
  }else{
    try{
      return self.queues.join(',');
    }catch(e){
      return '';
    }
  }
};

function prepareJobs(jobs) {
  return Object.keys(jobs).reduce(function(h, k) {
    var job = jobs[k];
    h[k] = typeof job === 'function' ? { perform: job } : job;
    return h;
  }, {});
}

exports.worker = worker;
<|endoftext|>


<commit_before>
var Urank = (function(){

    var _this,
        s = {},
        contentList, tagCloud, tagBox, visCanvas, docViewer,
        keywordExtractor, keywordExtractorOptions = { minRepetitions : 5 };
    // Color scales
    var tagColorRange = colorbrewer.Blues[TAG_CATEGORIES + 1].slice(1, TAG_CATEGORIES+1);
  //  tagColorRange.splice(tagColorRange.indexOf("#08519c"), 1, "#2171b5");
    var queryTermColorRange = colorbrewer.Set1[9];
    queryTermColorRange.splice(queryTermColorRange.indexOf("#ffff33"), 1, "#ffd700");

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    var EVTHANDLER = {

        onLoad: function(data) {

            //  Initialize keyword extractor
            keywordExtractorOptions.minRepetitions = (parseInt(data.length * 0.05) >= 5) ? parseInt(data.length * 0.05) : 5;
            keywordExtractor = new KeywordExtractor(keywordExtractorOptions);

            //  Clean documents and add them to the keyword extractor
            _this.data = typeof data == 'string' ? JSON.parse(data) : data;
            _this.data.forEach(function(d){
                d.title = d.title.clean();
                d.description = d.description.clean();
                var document = (d.description) ? d.title +'. '+ d.description : d.title;
                keywordExtractor.addDocument(document.removeUnnecessaryChars(), d.id);
            });

            //  Extract collection and document keywords
            keywordExtractor.processCollection();

            //  Assign document keywords
            _this.data.forEach(function(d, i){
                d.keywords = keywordExtractor.listDocumentKeywords(i);
            });

            //  Assign collection keywords and set other necessary variables
            _this.keywords = keywordExtractor.getCollectionKeywords();
            _this.rankingMode = RANKING_MODE.overall_score;
            _this.rankingModel.setData(_this.data);
            _this.selectedKeywords = [];
            _this.selectedId = STR_UNDEFINED;

            //  Build blocks
            contentList.build(_this.data);
            tagCloud.build(_this.keywords, _this.data.length);
            tagBox.build();
            visCanvas.build();
            docViewer.build();

            //  Bind event handlers to resize window and undo effects on random click
            $(window).off('resize', EVTHANDLER.onResize).resize(EVTHANDLER.onResize);
            $(s.root).off('mousedown', EVTHANDLER.onRootMouseDown).on('mousedown', EVTHANDLER.onRootMouseDown);

            //  Custom callback
            s.onLoad.call(this, _this.keywords);
        },

        onChange: function(selectedKeywords) {

            _this.selectedKeywords = selectedKeywords;
            _this.selectedId = STR_UNDEFINED;

            var rankingData = _this.rankingModel.update(_this.selectedKeywords, _this.rankingMode);
            var status = _this.rankingModel.getStatus();
            contentList.update(rankingData, status, _this.selectedKeywords, _this.queryTermColorScale);
            visCanvas.update(_this.rankingModel, $(s.contentListRoot).height(), _this.queryTermColorScale);
            docViewer.clear();
            tagCloud.removeEffects();

            s.onChange.call(this, rankingData, _this.selectedKeywords, status);
        },

        onTagDropped: function(index) {
            var queryTermColor = _this.queryTermColorScale(_this.keywords[index].stem);
            tagBox.dropTag(index, queryTermColor);
            s.onTagDropped.call(this, index, queryTermColor);
        },

        onTagDeleted: function(index) {
            tagBox.deleteTag(index);
            tagCloud.restoreTag(index);
            s.onTagDeleted.call(this, index);
        },

        onTagInCloudMouseEnter: function(index) {
            tagCloud.hoverTag(index);
            s.onTagInCloudMouseEnter.call(this, index);
        },

        onTagInCloudMouseLeave: function(index) {
            tagCloud.unhoverTag(index);
            s.onTagInCloudMouseLeave.call(this, index);
        },

        onTagInCloudClick: function(index) {
            // TODO
            s.onTagInCloudClick.call(this, index);
        },

        onKeywordHintEnter: function(index) {
            tagCloud.keywordHintMouseEntered(index);
            s.onKeywordHintMouseEnter.call(this, index);
        },

        onKeywordHintLeave: function(index) {
            tagCloud.keywordHintMouseLeft(index);
            s.onKeywordHintMouseLeave.call(this, index);
        },

        onKeywordHintClick: function(index) {
            tagCloud.keywordHintClicked(index);
            s.onKeywordHintClick.call(this, index);
        },

        onDocumentHintClick: function(index) {
            tagCloud.documentHintClicked(index);
            var idsArray = _this.keywords[index].inDocument;
            contentList.highlightListItems(idsArray);
            visCanvas.highlightItems(idsArray);
            s.onDocumentHintClick.call(this, index);
        },

        onTagInBoxMouseEnter: function(index) {
            // TODO
            s.onTagInBoxMouseEnter.call(this, index);
        },

        onTagInBoxMouseLeave: function(index) {
            // TODO
            s.onTagInBoxMouseLeave.call(this, index);
        },

        onTagInBoxClick: function(index) {
            // TODO
            s.onTagInBoxClick.call(this, index);
        },

        onItemClicked : function(documentId) {
            _this.selectedId = _this.selectedId === documentId ? STR_UNDEFINED : documentId;
            if(_this.selectedId !== STR_UNDEFINED) {    // select
                contentList.selectListItem(documentId);
                visCanvas.selectItem(documentId);
                docViewer.showDocument(_this.rankingModel.getDocumentById(documentId), _this.selectedKeywords.map(function(k){return k.stem}), _this.queryTermColorScale);
            }
            else {                   // deselect
                contentList.deselectAllListItems();
                visCanvas.deselectAllItems();
                docViewer.clear();
            }
            tagCloud.removeEffects();
            s.onItemClicked.call(this, documentId);
        },

        onItemMouseEnter: function(documentId) {
            contentList.hover(documentId);
            visCanvas.hoverItem(documentId);
            s.onItemMouseEnter.call(this, documentId);
        },

        onItemMouseLeave: function(documentId) {
            contentList.unhover(documentId);
            visCanvas.unhoverItem(documentId);
            s.onItemMouseLeave.call(this, documentId);
        },

        onFaviconClicked: function(documentId){
            contentList.switchFaviconOnOrOff(documentId);
            s.onFaviconClicked.call(this, documentId);
        },

        onWatchiconClicked: function(documentId) {
            contentList.watchOrUnwatchListItem(documentId);
            s.onWatchiconClicked.call(this, documentId);
        },

        onRootMouseDown: function(event){
            event.stopPropagation();
            if(event.which == 1) {
                contentList.deselectAllListItems();
                contentList.hideUnrankedListItems(_this.rankingModel.getRanking());
                visCanvas.deselectAllItems();
                docViewer.clear();
                tagCloud.removeEffects();
            }
        },

        onResize: function(event) {
            visCanvas.resize();
        },

        // Event handlers to return
        onRankByOverallScore: function() {
            _this.rankingMode = RANKING_MODE.overall_score;
            EVTHANDLER.onChange(_this.selectedKeywords);
            s.onRankByOverallScore.call(this);
        },

        onRankByMaximumScore: function() {
            _this.rankingMode = RANKING_MODE.max_score;
            EVTHANDLER.onChange(_this.selectedKeywords);
            s.onRankByMaximumScore.call(this);
        },

        onReset: function() {
            _this.rankingModel.reset();
            contentList.reset();
            tagCloud.reset();
            tagBox.clear();
            visCanvas.clear();
            docViewer.clear();
            _this.selectedId = STR_UNDEFINED;
            _this.selectedKeywords = [];
            s.onReset.call(this);
        },

        onDestroy: function() {
            tagCloud.destroy();
            tagBox.destroy();
            contentList.destroy();
            visCanvas.destroy();
            docViewer.destroy();
        },

        onClear: function() {
            tagCloud.clear();
            tagBox.clear();
            docViewer.clear();
            contentList.destroy();
            visCanvas.destroy();
        }
    };

    // Constructor
    function Urank(arguments) {

        _this = this;

        // default user-defined arguments
        s = $.extend({
            root: 'body',
            tagCloudRoot: '',
            tagBoxRoot: '',
            contentListRoot: '',
            visCanvasRoot: '',
            docViewerRoot: '',
            tagColorArray: tagColorRange,
            queryTermColorArray: queryTermColorRange,
            onLoad: function(keywords){},
            onChange: function(rankingData, selecedKeywords){},
            onItemClicked: function(documentId){},
            onItemMouseEnter: function(documentId){},
            onItemMouseLeave: function(documentId){},
            onFaviconClicked: function(documentId){},
            onWatchiconClicked: function(documentId){},
            onTagInCloudMouseEnter: function(index){},
            onTagInCloudMouseLeave: function(index){},
            onTagInCloudClick: function(index){},
            onDocumentHintClick: function(index){},
            onKeywordHintMouseEnter: function(index){},
            onKeywordHintMouseLeave: function(index){},
            onKeywordHintClick: function(index){},
            onTagDeleted: function(index){},
            onTagDropped: function(index, queryTermColor){},
            onTagInBoxMouseEnter: function(index){},
            onTagInBoxMouseLeave: function(index){},
            onTagInBoxClick: function(index){},
            onReset: function(){},
            onRankByOverallScore: function(){},
            onRankByMaximumScore: function(){}
        }, arguments);

        // Set color scales
        s.tagColorArray = s.tagColorArray.length >= TAG_CATEGORIES ? s.tagColorArray : tagColorRange;
        this.tagColorScale = d3.scale.ordinal().domain(d3.range(0, TAG_CATEGORIES, 1)).range(s.tagColorArray);
        s.queryTermColorArray = s.queryTermColorArray.length >= TAG_CATEGORIES ? s.queryTermColorArray : queryTermColorRange;
        this.queryTermColorScale = d3.scale.ordinal().range(s.queryTermColorArray);

        var options = {
            contentList: {
                root: s.contentListRoot,
                onItemClicked: EVTHANDLER.onItemClicked,
                onItemMouseEnter: EVTHANDLER.onItemMouseEnter,
                onItemMouseLeave: EVTHANDLER.onItemMouseLeave,
                onFaviconClicked: EVTHANDLER.onFaviconClicked,
                onWatchiconClicked: EVTHANDLER.onWatchiconClicked
            },

            tagCloud: {
                root: s.tagCloudRoot,
                colorScale: this.tagColorScale,
                dropIn: s.tagBoxRoot,
                onTagInCloudMouseEnter: EVTHANDLER.onTagInCloudMouseEnter,
                onTagInCloudMouseLeave: EVTHANDLER.onTagInCloudMouseLeave,
                onTagInCloudClick: EVTHANDLER.onTagInCloudClick,
                onDocumentHintClick: EVTHANDLER.onDocumentHintClick,
                onKeywordHintMouseEnter : EVTHANDLER.onKeywordHintEnter,
                onKeywordHintMouseLeave : EVTHANDLER.onKeywordHintLeave,
                onKeywordHintClick : EVTHANDLER.onKeywordHintClick
            },

            tagBox: {
                root: s.tagBoxRoot,
                colorScale: _this.queryTermColorScale,
                onChange: EVTHANDLER.onChange,
                onTagDropped: EVTHANDLER.onTagDropped,
                onTagDeleted: EVTHANDLER.onTagDeleted,
                onTagInBoxMouseEnter: EVTHANDLER.onTagInBoxMouseEnter,
                onTagInBoxMouseLeave: EVTHANDLER.onTagInBoxMouseLeave,
                onTagInBoxClick: EVTHANDLER.onTagInBoxClick
            },

            visCanvas: {
                root: s.visCanvasRoot,
                //visModule: VIS_MODULES.ranking,
                onItemClicked: EVTHANDLER.onItemClicked,
                onItemMouseEnter: EVTHANDLER.onItemMouseEnter,
                onItemMouseLeave: EVTHANDLER.onItemMouseLeave
            },

            docViewer: {
                root: s.docViewerRoot,
            }
        };

        this.rankingModel = new RankingModel();

        contentList = new ContentList(options.contentList);
        tagCloud = new TagCloud(options.tagCloud);
        tagBox = new TagBox(options.tagBox);
        visCanvas = new VisCanvas(options.visCanvas);
        docViewer = new DocViewer(options.docViewer);
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  Miscelaneous

    var _getUrankState = function(){
        return {
            mode: _this.rankingMode,
            status: _this.rankingModel.getStatus(),
            selectedKeywords: _this.selectedKeywords,
            ranking: _this.rankingModel.getRanking().map(function(d){
                return {
                    id: d.id,
                    title: d.title,
                    rankingPos: d.rankingPos,
                    overallScore: d.overallScore,
                    maxScore: d.maxScore,
                    weightedKeywords: d.weightedKeywords,
                    lastIndex: d.lastIndex,
                    positionsChanged: d.positionsChanged
                }
            })
        };
    };

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  Prototype

    Urank.prototype = {
        loadData: EVTHANDLER.onLoad,
        reset: EVTHANDLER.onReset,
        rankByOverallScore: EVTHANDLER.onRankByOverallScore,
        rankByMaximumScore: EVTHANDLER.onRankByMaximumScore,
        clear: EVTHANDLER.onClear,
        destroy: EVTHANDLER.onDestroy,
        getUrankState: _getUrankState
    };

    return Urank;
})();
<commit_msg>minor in getUrankState<commit_after>
var Urank = (function(){

    var _this,
        s = {},
        contentList, tagCloud, tagBox, visCanvas, docViewer,
        keywordExtractor, keywordExtractorOptions = { minRepetitions : 5 };
    // Color scales
    var tagColorRange = colorbrewer.Blues[TAG_CATEGORIES + 1].slice(1, TAG_CATEGORIES+1);
  //  tagColorRange.splice(tagColorRange.indexOf("#08519c"), 1, "#2171b5");
    var queryTermColorRange = colorbrewer.Set1[9];
    queryTermColorRange.splice(queryTermColorRange.indexOf("#ffff33"), 1, "#ffd700");

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    var EVTHANDLER = {

        onLoad: function(data) {

            //  Initialize keyword extractor
            keywordExtractorOptions.minRepetitions = (parseInt(data.length * 0.05) >= 5) ? parseInt(data.length * 0.05) : 5;
            keywordExtractor = new KeywordExtractor(keywordExtractorOptions);

            //  Clean documents and add them to the keyword extractor
            _this.data = typeof data == 'string' ? JSON.parse(data) : data;
            _this.data.forEach(function(d){
                d.title = d.title.clean();
                d.description = d.description.clean();
                var document = (d.description) ? d.title +'. '+ d.description : d.title;
                keywordExtractor.addDocument(document.removeUnnecessaryChars(), d.id);
            });

            //  Extract collection and document keywords
            keywordExtractor.processCollection();

            //  Assign document keywords
            _this.data.forEach(function(d, i){
                d.keywords = keywordExtractor.listDocumentKeywords(i);
            });

            //  Assign collection keywords and set other necessary variables
            _this.keywords = keywordExtractor.getCollectionKeywords();
            _this.rankingMode = RANKING_MODE.overall_score;
            _this.rankingModel.setData(_this.data);
            _this.selectedKeywords = [];
            _this.selectedId = STR_UNDEFINED;

            //  Build blocks
            contentList.build(_this.data);
            tagCloud.build(_this.keywords, _this.data.length);
            tagBox.build();
            visCanvas.build();
            docViewer.build();

            //  Bind event handlers to resize window and undo effects on random click
            $(window).off('resize', EVTHANDLER.onResize).resize(EVTHANDLER.onResize);
            $(s.root).off('mousedown', EVTHANDLER.onRootMouseDown).on('mousedown', EVTHANDLER.onRootMouseDown);

            //  Custom callback
            s.onLoad.call(this, _this.keywords);
        },

        onChange: function(selectedKeywords) {

            _this.selectedKeywords = selectedKeywords;
            _this.selectedId = STR_UNDEFINED;

            var rankingData = _this.rankingModel.update(_this.selectedKeywords, _this.rankingMode);
            var status = _this.rankingModel.getStatus();
            contentList.update(rankingData, status, _this.selectedKeywords, _this.queryTermColorScale);
            visCanvas.update(_this.rankingModel, $(s.contentListRoot).height(), _this.queryTermColorScale);
            docViewer.clear();
            tagCloud.removeEffects();

            s.onChange.call(this, rankingData, _this.selectedKeywords, status);
        },

        onTagDropped: function(index) {
            var queryTermColor = _this.queryTermColorScale(_this.keywords[index].stem);
            tagBox.dropTag(index, queryTermColor);
            s.onTagDropped.call(this, index, queryTermColor);
        },

        onTagDeleted: function(index) {
            tagBox.deleteTag(index);
            tagCloud.restoreTag(index);
            s.onTagDeleted.call(this, index);
        },

        onTagInCloudMouseEnter: function(index) {
            tagCloud.hoverTag(index);
            s.onTagInCloudMouseEnter.call(this, index);
        },

        onTagInCloudMouseLeave: function(index) {
            tagCloud.unhoverTag(index);
            s.onTagInCloudMouseLeave.call(this, index);
        },

        onTagInCloudClick: function(index) {
            // TODO
            s.onTagInCloudClick.call(this, index);
        },

        onKeywordHintEnter: function(index) {
            tagCloud.keywordHintMouseEntered(index);
            s.onKeywordHintMouseEnter.call(this, index);
        },

        onKeywordHintLeave: function(index) {
            tagCloud.keywordHintMouseLeft(index);
            s.onKeywordHintMouseLeave.call(this, index);
        },

        onKeywordHintClick: function(index) {
            tagCloud.keywordHintClicked(index);
            s.onKeywordHintClick.call(this, index);
        },

        onDocumentHintClick: function(index) {
            tagCloud.documentHintClicked(index);
            var idsArray = _this.keywords[index].inDocument;
            contentList.highlightListItems(idsArray);
            visCanvas.highlightItems(idsArray);
            s.onDocumentHintClick.call(this, index);
        },

        onTagInBoxMouseEnter: function(index) {
            // TODO
            s.onTagInBoxMouseEnter.call(this, index);
        },

        onTagInBoxMouseLeave: function(index) {
            // TODO
            s.onTagInBoxMouseLeave.call(this, index);
        },

        onTagInBoxClick: function(index) {
            // TODO
            s.onTagInBoxClick.call(this, index);
        },

        onItemClicked : function(documentId) {
            _this.selectedId = _this.selectedId === documentId ? STR_UNDEFINED : documentId;
            if(_this.selectedId !== STR_UNDEFINED) {    // select
                contentList.selectListItem(documentId);
                visCanvas.selectItem(documentId);
                docViewer.showDocument(_this.rankingModel.getDocumentById(documentId), _this.selectedKeywords.map(function(k){return k.stem}), _this.queryTermColorScale);
            }
            else {                   // deselect
                contentList.deselectAllListItems();
                visCanvas.deselectAllItems();
                docViewer.clear();
            }
            tagCloud.removeEffects();
            s.onItemClicked.call(this, documentId);
        },

        onItemMouseEnter: function(documentId) {
            contentList.hover(documentId);
            visCanvas.hoverItem(documentId);
            s.onItemMouseEnter.call(this, documentId);
        },

        onItemMouseLeave: function(documentId) {
            contentList.unhover(documentId);
            visCanvas.unhoverItem(documentId);
            s.onItemMouseLeave.call(this, documentId);
        },

        onFaviconClicked: function(documentId){
            contentList.switchFaviconOnOrOff(documentId);
            s.onFaviconClicked.call(this, documentId);
        },

        onWatchiconClicked: function(documentId) {
            contentList.watchOrUnwatchListItem(documentId);
            s.onWatchiconClicked.call(this, documentId);
        },

        onRootMouseDown: function(event){
            event.stopPropagation();
            if(event.which == 1) {
                contentList.deselectAllListItems();
                contentList.hideUnrankedListItems(_this.rankingModel.getRanking());
                visCanvas.deselectAllItems();
                docViewer.clear();
                tagCloud.removeEffects();
            }
        },

        onResize: function(event) {
            visCanvas.resize();
        },

        // Event handlers to return
        onRankByOverallScore: function() {
            _this.rankingMode = RANKING_MODE.overall_score;
            EVTHANDLER.onChange(_this.selectedKeywords);
            s.onRankByOverallScore.call(this);
        },

        onRankByMaximumScore: function() {
            _this.rankingMode = RANKING_MODE.max_score;
            EVTHANDLER.onChange(_this.selectedKeywords);
            s.onRankByMaximumScore.call(this);
        },

        onReset: function() {
            _this.rankingModel.reset();
            contentList.reset();
            tagCloud.reset();
            tagBox.clear();
            visCanvas.clear();
            docViewer.clear();
            _this.selectedId = STR_UNDEFINED;
            _this.selectedKeywords = [];
            s.onReset.call(this);
        },

        onDestroy: function() {
            tagCloud.destroy();
            tagBox.destroy();
            contentList.destroy();
            visCanvas.destroy();
            docViewer.destroy();
        },

        onClear: function() {
            tagCloud.clear();
            tagBox.clear();
            docViewer.clear();
            contentList.destroy();
            visCanvas.destroy();
        }
    };

    // Constructor
    function Urank(arguments) {

        _this = this;

        // default user-defined arguments
        s = $.extend({
            root: 'body',
            tagCloudRoot: '',
            tagBoxRoot: '',
            contentListRoot: '',
            visCanvasRoot: '',
            docViewerRoot: '',
            tagColorArray: tagColorRange,
            queryTermColorArray: queryTermColorRange,
            onLoad: function(keywords){},
            onChange: function(rankingData, selecedKeywords){},
            onItemClicked: function(documentId){},
            onItemMouseEnter: function(documentId){},
            onItemMouseLeave: function(documentId){},
            onFaviconClicked: function(documentId){},
            onWatchiconClicked: function(documentId){},
            onTagInCloudMouseEnter: function(index){},
            onTagInCloudMouseLeave: function(index){},
            onTagInCloudClick: function(index){},
            onDocumentHintClick: function(index){},
            onKeywordHintMouseEnter: function(index){},
            onKeywordHintMouseLeave: function(index){},
            onKeywordHintClick: function(index){},
            onTagDeleted: function(index){},
            onTagDropped: function(index, queryTermColor){},
            onTagInBoxMouseEnter: function(index){},
            onTagInBoxMouseLeave: function(index){},
            onTagInBoxClick: function(index){},
            onReset: function(){},
            onRankByOverallScore: function(){},
            onRankByMaximumScore: function(){}
        }, arguments);

        // Set color scales
        s.tagColorArray = s.tagColorArray.length >= TAG_CATEGORIES ? s.tagColorArray : tagColorRange;
        this.tagColorScale = d3.scale.ordinal().domain(d3.range(0, TAG_CATEGORIES, 1)).range(s.tagColorArray);
        s.queryTermColorArray = s.queryTermColorArray.length >= TAG_CATEGORIES ? s.queryTermColorArray : queryTermColorRange;
        this.queryTermColorScale = d3.scale.ordinal().range(s.queryTermColorArray);

        var options = {
            contentList: {
                root: s.contentListRoot,
                onItemClicked: EVTHANDLER.onItemClicked,
                onItemMouseEnter: EVTHANDLER.onItemMouseEnter,
                onItemMouseLeave: EVTHANDLER.onItemMouseLeave,
                onFaviconClicked: EVTHANDLER.onFaviconClicked,
                onWatchiconClicked: EVTHANDLER.onWatchiconClicked
            },

            tagCloud: {
                root: s.tagCloudRoot,
                colorScale: this.tagColorScale,
                dropIn: s.tagBoxRoot,
                onTagInCloudMouseEnter: EVTHANDLER.onTagInCloudMouseEnter,
                onTagInCloudMouseLeave: EVTHANDLER.onTagInCloudMouseLeave,
                onTagInCloudClick: EVTHANDLER.onTagInCloudClick,
                onDocumentHintClick: EVTHANDLER.onDocumentHintClick,
                onKeywordHintMouseEnter : EVTHANDLER.onKeywordHintEnter,
                onKeywordHintMouseLeave : EVTHANDLER.onKeywordHintLeave,
                onKeywordHintClick : EVTHANDLER.onKeywordHintClick
            },

            tagBox: {
                root: s.tagBoxRoot,
                colorScale: _this.queryTermColorScale,
                onChange: EVTHANDLER.onChange,
                onTagDropped: EVTHANDLER.onTagDropped,
                onTagDeleted: EVTHANDLER.onTagDeleted,
                onTagInBoxMouseEnter: EVTHANDLER.onTagInBoxMouseEnter,
                onTagInBoxMouseLeave: EVTHANDLER.onTagInBoxMouseLeave,
                onTagInBoxClick: EVTHANDLER.onTagInBoxClick
            },

            visCanvas: {
                root: s.visCanvasRoot,
                //visModule: VIS_MODULES.ranking,
                onItemClicked: EVTHANDLER.onItemClicked,
                onItemMouseEnter: EVTHANDLER.onItemMouseEnter,
                onItemMouseLeave: EVTHANDLER.onItemMouseLeave
            },

            docViewer: {
                root: s.docViewerRoot,
            }
        };

        this.rankingModel = new RankingModel();

        contentList = new ContentList(options.contentList);
        tagCloud = new TagCloud(options.tagCloud);
        tagBox = new TagBox(options.tagBox);
        visCanvas = new VisCanvas(options.visCanvas);
        docViewer = new DocViewer(options.docViewer);
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  Miscelaneous

    var _getUrankState = function(){
        return {
            mode: _this.rankingMode,
            status: _this.rankingModel.getStatus(),
            selectedKeywords: _this.selectedKeywords,
            ranking: _this.rankingModel.getRanking().map(function(d){
                return {
                    id: d.id,
                    title: d.title,
                    rankingPos: d.rankingPos,
                    overallScore: d.overallScore,
                    maxScore: d.maxScore,
                    lastIndex: d.lastIndex,
                    positionsChanged: d.positionsChanged,
                    weightedKeywords: d.weightedKeywords.map(function(wk){ return { term: wk.term, weightedScore: wk.weightedScore }; })
                }
            })
        };
    };

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  Prototype

    Urank.prototype = {
        loadData: EVTHANDLER.onLoad,
        reset: EVTHANDLER.onReset,
        rankByOverallScore: EVTHANDLER.onRankByOverallScore,
        rankByMaximumScore: EVTHANDLER.onRankByMaximumScore,
        clear: EVTHANDLER.onClear,
        destroy: EVTHANDLER.onDestroy,
        getUrankState: _getUrankState
    };

    return Urank;
})();
<|endoftext|>