<commit_before>(function () {

  'use strict';

  var path      =   require('path');
  var assert    =   require('assert');

  var mocha     =   require('mocha');
  var should    =   require('should');

  var prefix    =   process.env.EX_RE_CA_PREFIX || 'erct:';
  var host      =   process.env.EX_RE_CA_HOST || 'localhost';
  var port      =   process.env.EX_RE_CA_PORT || 6379;

  var _name     =   'test1';
  var _body     =   'test1 test1 test1';
  var _type     =   'text/plain';

  var cache     =   require('../')({
    prefix: prefix,
    host: host,
    port: port
  });

  describe ( 'get', function () {

    var error, results;

    it ( 'should be a function', function () {
      cache.get.should.be.a.Function;
    });

    it ( 'should callback', function (done) {
      cache.get(null, function ($error, $results) {
        error = $error;
        results = $results;
        done();
      });
    });

    it ( 'should not have error', function () {
      should(error).be.null;
    });

    it ( 'should be an array', function () {
      results.should.be.an.Array;
    });

    it ( ' - entry which has a property name which a string', function () {
      results.forEach(function (result) {
        result.name.should.be.a.String;
      });
    });

    it ( ' - entry which has a property body which a string', function () {
      results.forEach(function (result) {
        result.body.should.be.a.String;
      });
    });

    it ( ' - entry which has a property type which a string', function () {
      results.forEach(function (result) {
        result.type.should.be.a.String;
      });
    });

    it ( ' - entry which has a property touched which is a number which resolves to date', function () {
      results.forEach(function (result) {
        Number(result.touched).should.be.a.Number;

        new Date(Number(result.touched))
          .should.be.a.Date;
      });
    });

  });

})();
<commit_msg>Add tests to ensure wildcard functionality and avoiding KEYS call New test to check wildcard keys still fetch multiple results. New test to check that a non-wildcard call does not call KEYS.<commit_after>(function () {

  'use strict';

  var path      =   require('path');
  var assert    =   require('assert');

  var mocha     =   require('mocha');
  var should    =   require('should');

  var prefix    =   process.env.EX_RE_CA_PREFIX || 'erct:';
  var host      =   process.env.EX_RE_CA_HOST || 'localhost';
  var port      =   process.env.EX_RE_CA_PORT || 6379;

  var _name     =   'test1';
  var _body     =   'test1 test1 test1';
  var _type     =   'text/plain';

  var cache     =   require('../')({
    prefix: prefix,
    host: host,
    port: port
  });

  describe ( 'get', function () {

    var error, results;

    it ( 'should be a function', function () {
      cache.get.should.be.a.Function;
    });

    it ( 'should callback', function (done) {
      cache.get(null, function ($error, $results) {
        error = $error;
        results = $results;
        done();
      });
    });

    it ( 'should not have error', function () {
      should(error).be.null;
    });

    it ( 'should be an array', function () {
      results.should.be.an.Array;
    });

    it ( ' - entry which has a property name which a string', function () {
      results.forEach(function (result) {
        result.name.should.be.a.String;
      });
    });

    it ( ' - entry which has a property body which a string', function () {
      results.forEach(function (result) {
        result.body.should.be.a.String;
      });
    });

    it ( ' - entry which has a property type which a string', function () {
      results.forEach(function (result) {
        result.type.should.be.a.String;
      });
    });

    it ( ' - entry which has a property touched which is a number which resolves to date', function () {
      results.forEach(function (result) {
        Number(result.touched).should.be.a.Number;

        new Date(Number(result.touched))
          .should.be.a.Date;
      });
    });

    it ( 'should support wildcard gets', function (done) {
      cache.add('wildkey1', 'abc',
        function ($error, $name, $entry) {
          cache.add('wildkey2', 'def',
            function ($error, $name, $entry) {
              cache.get('wildkey*', function ($error, $results) {
                $results.should.be.an.Array;
                $results.should.have.a.lengthOf(2);
                done();
              });
            });
        });
    });

    it ( 'should support specific gets without calling keys', function (done) {

      // wrap the call to keys, so we can see if it's called
      var callCount = 0;
      var wrap = function(fn){
        return function(){
          console.log('What!?');
          callCount++;
          return fn.apply(this, arguments);
        };
      };

      cache.client.keys = wrap(cache.client.keys);

      cache.add('wildkey1', 'abc',
        function ($error, $name, $entry) {
          cache.add('wildkey2', 'def',
            function ($error, $name, $entry) {
              cache.get('wildkey1', function ($error, $results) {
                try {
                  $results.should.be.an.Array;
                  $results.should.have.a.lengthOf(1);
                  callCount.should.equal(0);
                  done();
                } catch (e) {
                  done(e);
                }
              });
            });
        });
    });
  });

})();
<|endoftext|>


<commit_before>

var head_conf = {

};

// this is head.load.min.js (v0.99)
/*!
 * HeadJS     The only script in your <HEAD>
 * Author     Tero Piirainen  (tipiirai)
 * Maintainer Robert Hoffmann (itechnology)
 * License    MIT / http://bit.ly/mit-license
 *
 * Version 0.99
 * http://headjs.com
 */
(function(f,w){function m(){}function g(a,b){if(a){"object"===typeof a&&(a=[].slice.call(a));for(var c=0,d=a.length;c<d;c++)b.call(a,a[c],c)}}function v(a,b){var c=Object.prototype.toString.call(b).slice(8,-1);return b!==w&&null!==b&&c===a}function k(a){return v("Function",a)}function h(a){a=a||m;a._done||(a(),a._done=1)}function n(a){var b={};if("object"===typeof a)for(var c in a)a[c]&&(b={name:c,url:a[c]});else b=a.split("/"),b=b[b.length-1],c=b.indexOf("?"),b={name:-1!==c?b.substring(0,c):b,url:a};
return(a=p[b.name])&&a.url===b.url?a:p[b.name]=b}function q(a){var a=a||p,b;for(b in a)if(a.hasOwnProperty(b)&&a[b].state!==r)return!1;return!0}function s(a,b){b=b||m;a.state===r?b():a.state===x?d.ready(a.name,b):a.state===y?a.onpreload.push(function(){s(a,b)}):(a.state=x,z(a,function(){a.state=r;b();g(l[a.name],function(a){h(a)});j&&q()&&g(l.ALL,function(a){h(a)})}))}function z(a,b){var b=b||m,c;/\.css[^\.]*$/.test(a.url)?(c=e.createElement("link"),c.type="text/"+(a.type||"css"),c.rel="stylesheet",
c.href=a.url):(c=e.createElement("script"),c.type="text/"+(a.type||"javascript"),c.src=a.url);c.onload=c.onreadystatechange=function(a){a=a||f.event;if("load"===a.type||/loaded|complete/.test(c.readyState)&&(!e.documentMode||9>e.documentMode))c.onload=c.onreadystatechange=c.onerror=null,b()};c.onerror=function(){c.onload=c.onreadystatechange=c.onerror=null;b()};c.async=!1;c.defer=!1;var d=e.head||e.getElementsByTagName("head")[0];d.insertBefore(c,d.lastChild)}function i(){e.body?j||(j=!0,g(A,function(a){h(a)})):
(f.clearTimeout(d.readyTimeout),d.readyTimeout=f.setTimeout(i,50))}function t(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",t,!1),i()):"complete"===e.readyState&&(e.detachEvent("onreadystatechange",t),i())}var e=f.document,A=[],B=[],l={},p={},E="async"in e.createElement("script")||"MozAppearance"in e.documentElement.style||f.opera,C,j,D=f.head_conf&&f.head_conf.head||"head",d=f[D]=f[D]||function(){d.ready.apply(null,arguments)},y=1,x=3,r=4;d.load=E?function(){var a=arguments,b=a[a.length-
1],c={};k(b)||(b=null);g(a,function(d,e){d!==b&&(d=n(d),c[d.name]=d,s(d,b&&e===a.length-2?function(){q(c)&&h(b)}:null))});return d}:function(){var a=arguments,b=[].slice.call(a,1),c=b[0];if(!C)return B.push(function(){d.load.apply(null,a)}),d;c?(g(b,function(a){if(!k(a)){var b=n(a);b.state===w&&(b.state=y,b.onpreload=[],z({url:b.url,type:"cache"},function(){b.state=2;g(b.onpreload,function(a){a.call()})}))}}),s(n(a[0]),k(c)?c:function(){d.load.apply(null,b)})):s(n(a[0]));return d};d.js=d.load;d.test=
function(a,b,c,e){a="object"===typeof a?a:{test:a,success:b?v("Array",b)?b:[b]:!1,failure:c?v("Array",c)?c:[c]:!1,callback:e||m};(b=!!a.test)&&a.success?(a.success.push(a.callback),d.load.apply(null,a.success)):!b&&a.failure?(a.failure.push(a.callback),d.load.apply(null,a.failure)):e();return d};d.ready=function(a,b){if(a===e)return j?h(b):A.push(b),d;k(a)&&(b=a,a="ALL");if("string"!==typeof a||!k(b))return d;var c=p[a];if(c&&c.state===r||"ALL"===a&&q()&&j)return h(b),d;(c=l[a])?c.push(b):l[a]=[b];
return d};d.ready(e,function(){q()&&g(l.ALL,function(a){h(a)});d.feature&&d.feature("domloaded",!0)});if("complete"===e.readyState)i();else if(e.addEventListener)e.addEventListener("DOMContentLoaded",t,!1),f.addEventListener("load",i,!1);else{e.attachEvent("onreadystatechange",t);f.attachEvent("onload",i);var u=!1;try{u=null==f.frameElement&&e.documentElement}catch(F){}u&&u.doScroll&&function b(){if(!j){try{u.doScroll("left")}catch(c){f.clearTimeout(d.readyTimeout);d.readyTimeout=f.setTimeout(b,50);
return}i()}}()}setTimeout(function(){C=!0;g(B,function(b){b()})},300)})(window);

// this is the actual includedeck

function includedeck(m, c, a) {
    var modules = m || [];
    var conf = c || {};
    var atExit = a || [];
    var deckSelector = conf.DECK || '.slide';

    var info = {
        "deck": [
            "deck.js/jquery-1.7.2.min.js",
	    "deck.js/core/deck.core.css",
            "deck.js/modernizr.custom.js",
            "deck.js/core/deck.core.js"
        ],
        "fit": [
            "deck.js/extensions/fit/deck.fit.js"
        ],
        "fit-fs": [
            "deck.js/extensions/fit/deck.fit-fs.css",
            "deck.js/extensions/fit/deck.fit.js" // "@fit" <- todo replace by this
        ],
        "simplemath": [
            "deck.js/libs/display-latex2.user.js",
            "deck.js/extensions/simplemath/deck.simplemath.js"
        ],
        "smartsyntax": [
            "deck.js/extensions/smartsyntax/deck.smartsyntax.js"
        ],
        // todo add some: clone, goto, navigation, menu, hash, svg, anim, (and maybe some subparts of anim)
        dummy: {} // TODO add a newdeck
    };

    // todo: recursively process info to make metapackages (@...)
    // todo handle default extensions (deck.js/extensions/N/deck.N.{js,css}

    var toLoad = [];

    modules = ["deck"].concat(modules);

    for (i in modules) {
        toLoad = toLoad.concat(info[modules[i]]);
    }

    head.js.apply(head,toLoad);
    head.ready(function() {
        $.deck(deckSelector, conf);
    });
}

<commit_msg>[includedeck] handling all extensions from the current bare template<commit_after>

var head_conf = {

};

// this is head.load.min.js (v0.99)
/*!
 * HeadJS     The only script in your <HEAD>
 * Author     Tero Piirainen  (tipiirai)
 * Maintainer Robert Hoffmann (itechnology)
 * License    MIT / http://bit.ly/mit-license
 *
 * Version 0.99
 * http://headjs.com
 */
(function(f,w){function m(){}function g(a,b){if(a){"object"===typeof a&&(a=[].slice.call(a));for(var c=0,d=a.length;c<d;c++)b.call(a,a[c],c)}}function v(a,b){var c=Object.prototype.toString.call(b).slice(8,-1);return b!==w&&null!==b&&c===a}function k(a){return v("Function",a)}function h(a){a=a||m;a._done||(a(),a._done=1)}function n(a){var b={};if("object"===typeof a)for(var c in a)a[c]&&(b={name:c,url:a[c]});else b=a.split("/"),b=b[b.length-1],c=b.indexOf("?"),b={name:-1!==c?b.substring(0,c):b,url:a};
return(a=p[b.name])&&a.url===b.url?a:p[b.name]=b}function q(a){var a=a||p,b;for(b in a)if(a.hasOwnProperty(b)&&a[b].state!==r)return!1;return!0}function s(a,b){b=b||m;a.state===r?b():a.state===x?d.ready(a.name,b):a.state===y?a.onpreload.push(function(){s(a,b)}):(a.state=x,z(a,function(){a.state=r;b();g(l[a.name],function(a){h(a)});j&&q()&&g(l.ALL,function(a){h(a)})}))}function z(a,b){var b=b||m,c;/\.css[^\.]*$/.test(a.url)?(c=e.createElement("link"),c.type="text/"+(a.type||"css"),c.rel="stylesheet",
c.href=a.url):(c=e.createElement("script"),c.type="text/"+(a.type||"javascript"),c.src=a.url);c.onload=c.onreadystatechange=function(a){a=a||f.event;if("load"===a.type||/loaded|complete/.test(c.readyState)&&(!e.documentMode||9>e.documentMode))c.onload=c.onreadystatechange=c.onerror=null,b()};c.onerror=function(){c.onload=c.onreadystatechange=c.onerror=null;b()};c.async=!1;c.defer=!1;var d=e.head||e.getElementsByTagName("head")[0];d.insertBefore(c,d.lastChild)}function i(){e.body?j||(j=!0,g(A,function(a){h(a)})):
(f.clearTimeout(d.readyTimeout),d.readyTimeout=f.setTimeout(i,50))}function t(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",t,!1),i()):"complete"===e.readyState&&(e.detachEvent("onreadystatechange",t),i())}var e=f.document,A=[],B=[],l={},p={},E="async"in e.createElement("script")||"MozAppearance"in e.documentElement.style||f.opera,C,j,D=f.head_conf&&f.head_conf.head||"head",d=f[D]=f[D]||function(){d.ready.apply(null,arguments)},y=1,x=3,r=4;d.load=E?function(){var a=arguments,b=a[a.length-
1],c={};k(b)||(b=null);g(a,function(d,e){d!==b&&(d=n(d),c[d.name]=d,s(d,b&&e===a.length-2?function(){q(c)&&h(b)}:null))});return d}:function(){var a=arguments,b=[].slice.call(a,1),c=b[0];if(!C)return B.push(function(){d.load.apply(null,a)}),d;c?(g(b,function(a){if(!k(a)){var b=n(a);b.state===w&&(b.state=y,b.onpreload=[],z({url:b.url,type:"cache"},function(){b.state=2;g(b.onpreload,function(a){a.call()})}))}}),s(n(a[0]),k(c)?c:function(){d.load.apply(null,b)})):s(n(a[0]));return d};d.js=d.load;d.test=
function(a,b,c,e){a="object"===typeof a?a:{test:a,success:b?v("Array",b)?b:[b]:!1,failure:c?v("Array",c)?c:[c]:!1,callback:e||m};(b=!!a.test)&&a.success?(a.success.push(a.callback),d.load.apply(null,a.success)):!b&&a.failure?(a.failure.push(a.callback),d.load.apply(null,a.failure)):e();return d};d.ready=function(a,b){if(a===e)return j?h(b):A.push(b),d;k(a)&&(b=a,a="ALL");if("string"!==typeof a||!k(b))return d;var c=p[a];if(c&&c.state===r||"ALL"===a&&q()&&j)return h(b),d;(c=l[a])?c.push(b):l[a]=[b];
return d};d.ready(e,function(){q()&&g(l.ALL,function(a){h(a)});d.feature&&d.feature("domloaded",!0)});if("complete"===e.readyState)i();else if(e.addEventListener)e.addEventListener("DOMContentLoaded",t,!1),f.addEventListener("load",i,!1);else{e.attachEvent("onreadystatechange",t);f.attachEvent("onload",i);var u=!1;try{u=null==f.frameElement&&e.documentElement}catch(F){}u&&u.doScroll&&function b(){if(!j){try{u.doScroll("left")}catch(c){f.clearTimeout(d.readyTimeout);d.readyTimeout=f.setTimeout(b,50);
return}i()}}()}setTimeout(function(){C=!0;g(B,function(b){b()})},300)})(window);

// this is the actual includedeck

function includedeck(m, c, a) {
    var modules = m || [];
    var conf = c || {};
    var atExit = a || [];
    var deckSelector = conf.DECK || '.slide';

    var info = {
        "deck": [
            "deck.js/jquery-1.7.2.min.js",
	    "deck.js/core/deck.core.css",
            "deck.js/modernizr.custom.js",
            "deck.js/core/deck.core.js"
        ],
        // ^ todo this is "core" deck, shoult it be included by default or not? also, provide an alternative with new css
        "fit": [ "deck.js/extensions/fit/deck.fit.js" ],
        "fit-fs": [
            "deck.js/extensions/fit/deck.fit-fs.css",
            "deck.js/extensions/fit/deck.fit.js" // "@fit" <- todo replace by this
        ],
        "simplemath": [
            "deck.js/libs/display-latex2.user.js",
            "deck.js/extensions/simplemath/deck.simplemath.js"
        ],
        "smartsyntax": [ "deck.js/extensions/smartsyntax/deck.smartsyntax.js" ],
        "clone": [ "deck.js/extensions/clone/deck.clone.js" ],
        "goto": [
            "deck.js/extensions/goto/deck.goto.css",
            "deck.js/extensions/goto/deck.goto.js"
        ],
        "status": [
            "deck.js/extensions/status/deck.status.css",
            "deck.js/extensions/status/deck.status.js"
        ],
        "navigation": [
            "deck.js/extensions/navigation/deck.navigation.css",
            "deck.js/extensions/navigation/deck.navigation.js"
        ],
        "menu": [
            "deck.js/extensions/menu/deck.menu.css",
            "deck.js/extensions/menu/deck.menu.js"
        ],
        "hash": [
            "deck.js/extensions/hash/deck.hash.css",
            "deck.js/extensions/hash/deck.hash.js"
        ],
        "step": [ "deck.js/extensions/step/deck.step.js" ],
        "events": [ "deck.js/extensions/events/deck.events.js" ],
        "anim": [ "deck.js/extensions/anim/deck.anim.js" ],
        "svg": [
            "deck.js/libs/jquerysvg/jquery.svg.min.js" ,
            "deck.js/libs/jquerysvg/jquery.svganim.min.js",
            "deck.js/extensions/svg/deck.svg.js"
        ],
        dummy: {} // TODO add a newdeck
    };

    // todo: recursively process info to make metapackages (@...)
    // todo: handle default extensions (deck.js/extensions/N/deck.N.{js,css}
    // todo: have a conf to change the base path

    var toLoad = [];

    modules = ["deck"].concat(modules);

    for (i in modules) {
        toLoad = toLoad.concat(info[modules[i]]);
    }

    head.js.apply(head,toLoad);
    head.ready(function() {
        $.deck(deckSelector, conf);
    });
}

<|endoftext|>


<commit_before>/**
 * LookupElement is a mixin that creates a {@link OO.ui.TextInputMenuSelectWidget menu} of suggested values for
 * a {@link OO.ui.TextInputWidget text input widget}. Suggested values are based on the characters the user types
 * into the text input field and, in general, the menu is only displayed when the user types. If a suggested value is chosen
 * from the lookup menu, that value becomes the value of the input field.
 *
 * Note that a new menu of suggested items is displayed when a value is chosen from the lookup menu. If this is
 * not the desired behavior, disable lookup menus with the #setLookupsDisabled method, then set the value, then
 * re-enable lookups.
 *
 * See the [OOjs UI demos][1] for an example.
 *
 * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/index.html#widgets-apex-vector-ltr
 *
 * @class
 * @abstract
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning
 * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element.
 * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty.
 *  By default, the lookup menu is not generated and displayed until the user begins to type.
 */
OO.ui.LookupElement = function OoUiLookupElement( config ) {
	// Configuration initialization
	config = config || {};

	// Properties
	this.$overlay = config.$overlay || this.$element;
	this.lookupMenu = new OO.ui.TextInputMenuSelectWidget( this, {
		widget: this,
		input: this,
		$container: config.$container
	} );

	this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false;

	this.lookupCache = {};
	this.lookupQuery = null;
	this.lookupRequest = null;
	this.lookupsDisabled = false;
	this.lookupInputFocused = false;

	// Events
	this.$input.on( {
		focus: this.onLookupInputFocus.bind( this ),
		blur: this.onLookupInputBlur.bind( this ),
		mousedown: this.onLookupInputMouseDown.bind( this )
	} );
	this.connect( this, { change: 'onLookupInputChange' } );
	this.lookupMenu.connect( this, {
		toggle: 'onLookupMenuToggle',
		choose: 'onLookupMenuItemChoose'
	} );

	// Initialization
	this.$element.addClass( 'oo-ui-lookupElement' );
	this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' );
	this.$overlay.append( this.lookupMenu.$element );
};

/* Methods */

/**
 * Handle input focus event.
 *
 * @protected
 * @param {jQuery.Event} e Input focus event
 */
OO.ui.LookupElement.prototype.onLookupInputFocus = function () {
	this.lookupInputFocused = true;
	this.populateLookupMenu();
};

/**
 * Handle input blur event.
 *
 * @protected
 * @param {jQuery.Event} e Input blur event
 */
OO.ui.LookupElement.prototype.onLookupInputBlur = function () {
	this.closeLookupMenu();
	this.lookupInputFocused = false;
};

/**
 * Handle input mouse down event.
 *
 * @protected
 * @param {jQuery.Event} e Input mouse down event
 */
OO.ui.LookupElement.prototype.onLookupInputMouseDown = function () {
	// Only open the menu if the input was already focused.
	// This way we allow the user to open the menu again after closing it with Esc
	// by clicking in the input. Opening (and populating) the menu when initially
	// clicking into the input is handled by the focus handler.
	if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) {
		this.populateLookupMenu();
	}
};

/**
 * Handle input change event.
 *
 * @protected
 * @param {string} value New input value
 */
OO.ui.LookupElement.prototype.onLookupInputChange = function () {
	if ( this.lookupInputFocused ) {
		this.populateLookupMenu();
	}
};

/**
 * Handle the lookup menu being shown/hidden.
 *
 * @protected
 * @param {boolean} visible Whether the lookup menu is now visible.
 */
OO.ui.LookupElement.prototype.onLookupMenuToggle = function ( visible ) {
	if ( !visible ) {
		// When the menu is hidden, abort any active request and clear the menu.
		// This has to be done here in addition to closeLookupMenu(), because
		// MenuSelectWidget will close itself when the user presses Esc.
		this.abortLookupRequest();
		this.lookupMenu.clearItems();
	}
};

/**
 * Handle menu item 'choose' event, updating the text input value to the value of the clicked item.
 *
 * @protected
 * @param {OO.ui.MenuOptionWidget} item Selected item
 */
OO.ui.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) {
	this.setValue( item.getData() );
};

/**
 * Get lookup menu.
 *
 * @private
 * @return {OO.ui.TextInputMenuSelectWidget}
 */
OO.ui.LookupElement.prototype.getLookupMenu = function () {
	return this.lookupMenu;
};

/**
 * Disable or re-enable lookups.
 *
 * When lookups are disabled, calls to #populateLookupMenu will be ignored.
 *
 * @param {boolean} disabled Disable lookups
 */
OO.ui.LookupElement.prototype.setLookupsDisabled = function ( disabled ) {
	this.lookupsDisabled = !!disabled;
};

/**
 * Open the menu. If there are no entries in the menu, this does nothing.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.openLookupMenu = function () {
	if ( !this.lookupMenu.isEmpty() ) {
		this.lookupMenu.toggle( true );
	}
	return this;
};

/**
 * Close the menu, empty it, and abort any pending request.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.closeLookupMenu = function () {
	this.lookupMenu.toggle( false );
	this.abortLookupRequest();
	this.lookupMenu.clearItems();
	return this;
};

/**
 * Request menu items based on the input's current value, and when they arrive,
 * populate the menu with these items and show the menu.
 *
 * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.populateLookupMenu = function () {
	var widget = this,
		value = this.getValue();

	if ( this.lookupsDisabled ) {
		return;
	}

	// If the input is empty, clear the menu, unless suggestions when empty are allowed.
	if ( !this.allowSuggestionsWhenEmpty && value === '' ) {
		this.closeLookupMenu();
	// Skip population if there is already a request pending for the current value
	} else if ( value !== this.lookupQuery ) {
		this.getLookupMenuItems()
			.done( function ( items ) {
				widget.lookupMenu.clearItems();
				if ( items.length ) {
					widget.lookupMenu
						.addItems( items )
						.toggle( true );
					widget.initializeLookupMenuSelection();
				} else {
					widget.lookupMenu.toggle( false );
				}
			} )
			.fail( function () {
				widget.lookupMenu.clearItems();
			} );
	}

	return this;
};

/**
 * Select and highlight the first selectable item in the menu.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.initializeLookupMenuSelection = function () {
	if ( !this.lookupMenu.getSelectedItem() ) {
		this.lookupMenu.selectItem( this.lookupMenu.getFirstSelectableItem() );
	}
	this.lookupMenu.highlightItem( this.lookupMenu.getSelectedItem() );
};

/**
 * Get lookup menu items for the current query.
 *
 * @private
 * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument of
 *   the done event. If the request was aborted to make way for a subsequent request, this promise
 *   will not be rejected: it will remain pending forever.
 */
OO.ui.LookupElement.prototype.getLookupMenuItems = function () {
	var widget = this,
		value = this.getValue(),
		deferred = $.Deferred(),
		ourRequest;

	this.abortLookupRequest();
	if ( Object.prototype.hasOwnProperty.call( this.lookupCache, value ) ) {
		deferred.resolve( this.getLookupMenuOptionsFromData( this.lookupCache[ value ] ) );
	} else {
		this.pushPending();
		this.lookupQuery = value;
		ourRequest = this.lookupRequest = this.getLookupRequest();
		ourRequest
			.always( function () {
				// We need to pop pending even if this is an old request, otherwise
				// the widget will remain pending forever.
				// TODO: this assumes that an aborted request will fail or succeed soon after
				// being aborted, or at least eventually. It would be nice if we could popPending()
				// at abort time, but only if we knew that we hadn't already called popPending()
				// for that request.
				widget.popPending();
			} )
			.done( function ( response ) {
				// If this is an old request (and aborting it somehow caused it to still succeed),
				// ignore its success completely
				if ( ourRequest === widget.lookupRequest ) {
					widget.lookupQuery = null;
					widget.lookupRequest = null;
					widget.lookupCache[ value ] = widget.getLookupCacheDataFromResponse( response );
					deferred.resolve( widget.getLookupMenuOptionsFromData( widget.lookupCache[ value ] ) );
				}
			} )
			.fail( function () {
				// If this is an old request (or a request failing because it's being aborted),
				// ignore its failure completely
				if ( ourRequest === widget.lookupRequest ) {
					widget.lookupQuery = null;
					widget.lookupRequest = null;
					deferred.reject();
				}
			} );
	}
	return deferred.promise();
};

/**
 * Abort the currently pending lookup request, if any.
 *
 * @private
 */
OO.ui.LookupElement.prototype.abortLookupRequest = function () {
	var oldRequest = this.lookupRequest;
	if ( oldRequest ) {
		// First unset this.lookupRequest to the fail handler will notice
		// that the request is no longer current
		this.lookupRequest = null;
		this.lookupQuery = null;
		oldRequest.abort();
	}
};

/**
 * Get a new request object of the current lookup query value.
 *
 * @protected
 * @abstract
 * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
 */
OO.ui.LookupElement.prototype.getLookupRequest = function () {
	// Stub, implemented in subclass
	return null;
};

/**
 * Pre-process data returned by the request from #getLookupRequest.
 *
 * The return value of this function will be cached, and any further queries for the given value
 * will use the cache rather than doing API requests.
 *
 * @protected
 * @abstract
 * @param {Mixed} response Response from server
 * @return {Mixed} Cached result data
 */
OO.ui.LookupElement.prototype.getLookupCacheDataFromResponse = function () {
	// Stub, implemented in subclass
	return [];
};

/**
 * Get a list of menu option widgets from the (possibly cached) data returned by
 * #getLookupCacheDataFromResponse.
 *
 * @protected
 * @abstract
 * @param {Mixed} data Cached result data, usually an array
 * @return {OO.ui.MenuOptionWidget[]} Menu items
 */
OO.ui.LookupElement.prototype.getLookupMenuOptionsFromData = function () {
	// Stub, implemented in subclass
	return [];
};
<commit_msg>Don't select lookup items on initialize<commit_after>/**
 * LookupElement is a mixin that creates a {@link OO.ui.TextInputMenuSelectWidget menu} of suggested values for
 * a {@link OO.ui.TextInputWidget text input widget}. Suggested values are based on the characters the user types
 * into the text input field and, in general, the menu is only displayed when the user types. If a suggested value is chosen
 * from the lookup menu, that value becomes the value of the input field.
 *
 * Note that a new menu of suggested items is displayed when a value is chosen from the lookup menu. If this is
 * not the desired behavior, disable lookup menus with the #setLookupsDisabled method, then set the value, then
 * re-enable lookups.
 *
 * See the [OOjs UI demos][1] for an example.
 *
 * [1]: https://tools.wmflabs.org/oojs-ui/oojs-ui/demos/index.html#widgets-apex-vector-ltr
 *
 * @class
 * @abstract
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning
 * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element.
 * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty.
 *  By default, the lookup menu is not generated and displayed until the user begins to type.
 */
OO.ui.LookupElement = function OoUiLookupElement( config ) {
	// Configuration initialization
	config = config || {};

	// Properties
	this.$overlay = config.$overlay || this.$element;
	this.lookupMenu = new OO.ui.TextInputMenuSelectWidget( this, {
		widget: this,
		input: this,
		$container: config.$container
	} );

	this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false;

	this.lookupCache = {};
	this.lookupQuery = null;
	this.lookupRequest = null;
	this.lookupsDisabled = false;
	this.lookupInputFocused = false;

	// Events
	this.$input.on( {
		focus: this.onLookupInputFocus.bind( this ),
		blur: this.onLookupInputBlur.bind( this ),
		mousedown: this.onLookupInputMouseDown.bind( this )
	} );
	this.connect( this, { change: 'onLookupInputChange' } );
	this.lookupMenu.connect( this, {
		toggle: 'onLookupMenuToggle',
		choose: 'onLookupMenuItemChoose'
	} );

	// Initialization
	this.$element.addClass( 'oo-ui-lookupElement' );
	this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' );
	this.$overlay.append( this.lookupMenu.$element );
};

/* Methods */

/**
 * Handle input focus event.
 *
 * @protected
 * @param {jQuery.Event} e Input focus event
 */
OO.ui.LookupElement.prototype.onLookupInputFocus = function () {
	this.lookupInputFocused = true;
	this.populateLookupMenu();
};

/**
 * Handle input blur event.
 *
 * @protected
 * @param {jQuery.Event} e Input blur event
 */
OO.ui.LookupElement.prototype.onLookupInputBlur = function () {
	this.closeLookupMenu();
	this.lookupInputFocused = false;
};

/**
 * Handle input mouse down event.
 *
 * @protected
 * @param {jQuery.Event} e Input mouse down event
 */
OO.ui.LookupElement.prototype.onLookupInputMouseDown = function () {
	// Only open the menu if the input was already focused.
	// This way we allow the user to open the menu again after closing it with Esc
	// by clicking in the input. Opening (and populating) the menu when initially
	// clicking into the input is handled by the focus handler.
	if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) {
		this.populateLookupMenu();
	}
};

/**
 * Handle input change event.
 *
 * @protected
 * @param {string} value New input value
 */
OO.ui.LookupElement.prototype.onLookupInputChange = function () {
	if ( this.lookupInputFocused ) {
		this.populateLookupMenu();
	}
};

/**
 * Handle the lookup menu being shown/hidden.
 *
 * @protected
 * @param {boolean} visible Whether the lookup menu is now visible.
 */
OO.ui.LookupElement.prototype.onLookupMenuToggle = function ( visible ) {
	if ( !visible ) {
		// When the menu is hidden, abort any active request and clear the menu.
		// This has to be done here in addition to closeLookupMenu(), because
		// MenuSelectWidget will close itself when the user presses Esc.
		this.abortLookupRequest();
		this.lookupMenu.clearItems();
	}
};

/**
 * Handle menu item 'choose' event, updating the text input value to the value of the clicked item.
 *
 * @protected
 * @param {OO.ui.MenuOptionWidget} item Selected item
 */
OO.ui.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) {
	this.setValue( item.getData() );
};

/**
 * Get lookup menu.
 *
 * @private
 * @return {OO.ui.TextInputMenuSelectWidget}
 */
OO.ui.LookupElement.prototype.getLookupMenu = function () {
	return this.lookupMenu;
};

/**
 * Disable or re-enable lookups.
 *
 * When lookups are disabled, calls to #populateLookupMenu will be ignored.
 *
 * @param {boolean} disabled Disable lookups
 */
OO.ui.LookupElement.prototype.setLookupsDisabled = function ( disabled ) {
	this.lookupsDisabled = !!disabled;
};

/**
 * Open the menu. If there are no entries in the menu, this does nothing.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.openLookupMenu = function () {
	if ( !this.lookupMenu.isEmpty() ) {
		this.lookupMenu.toggle( true );
	}
	return this;
};

/**
 * Close the menu, empty it, and abort any pending request.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.closeLookupMenu = function () {
	this.lookupMenu.toggle( false );
	this.abortLookupRequest();
	this.lookupMenu.clearItems();
	return this;
};

/**
 * Request menu items based on the input's current value, and when they arrive,
 * populate the menu with these items and show the menu.
 *
 * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.populateLookupMenu = function () {
	var widget = this,
		value = this.getValue();

	if ( this.lookupsDisabled ) {
		return;
	}

	// If the input is empty, clear the menu, unless suggestions when empty are allowed.
	if ( !this.allowSuggestionsWhenEmpty && value === '' ) {
		this.closeLookupMenu();
	// Skip population if there is already a request pending for the current value
	} else if ( value !== this.lookupQuery ) {
		this.getLookupMenuItems()
			.done( function ( items ) {
				widget.lookupMenu.clearItems();
				if ( items.length ) {
					widget.lookupMenu
						.addItems( items )
						.toggle( true );
					widget.initializeLookupMenuSelection();
				} else {
					widget.lookupMenu.toggle( false );
				}
			} )
			.fail( function () {
				widget.lookupMenu.clearItems();
			} );
	}

	return this;
};

/**
 * Highlight the first selectable item in the menu.
 *
 * @private
 * @chainable
 */
OO.ui.LookupElement.prototype.initializeLookupMenuSelection = function () {
	if ( !this.lookupMenu.getSelectedItem() ) {
		this.lookupMenu.highlightItem( this.lookupMenu.getFirstSelectableItem() );
	}
};

/**
 * Get lookup menu items for the current query.
 *
 * @private
 * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument of
 *   the done event. If the request was aborted to make way for a subsequent request, this promise
 *   will not be rejected: it will remain pending forever.
 */
OO.ui.LookupElement.prototype.getLookupMenuItems = function () {
	var widget = this,
		value = this.getValue(),
		deferred = $.Deferred(),
		ourRequest;

	this.abortLookupRequest();
	if ( Object.prototype.hasOwnProperty.call( this.lookupCache, value ) ) {
		deferred.resolve( this.getLookupMenuOptionsFromData( this.lookupCache[ value ] ) );
	} else {
		this.pushPending();
		this.lookupQuery = value;
		ourRequest = this.lookupRequest = this.getLookupRequest();
		ourRequest
			.always( function () {
				// We need to pop pending even if this is an old request, otherwise
				// the widget will remain pending forever.
				// TODO: this assumes that an aborted request will fail or succeed soon after
				// being aborted, or at least eventually. It would be nice if we could popPending()
				// at abort time, but only if we knew that we hadn't already called popPending()
				// for that request.
				widget.popPending();
			} )
			.done( function ( response ) {
				// If this is an old request (and aborting it somehow caused it to still succeed),
				// ignore its success completely
				if ( ourRequest === widget.lookupRequest ) {
					widget.lookupQuery = null;
					widget.lookupRequest = null;
					widget.lookupCache[ value ] = widget.getLookupCacheDataFromResponse( response );
					deferred.resolve( widget.getLookupMenuOptionsFromData( widget.lookupCache[ value ] ) );
				}
			} )
			.fail( function () {
				// If this is an old request (or a request failing because it's being aborted),
				// ignore its failure completely
				if ( ourRequest === widget.lookupRequest ) {
					widget.lookupQuery = null;
					widget.lookupRequest = null;
					deferred.reject();
				}
			} );
	}
	return deferred.promise();
};

/**
 * Abort the currently pending lookup request, if any.
 *
 * @private
 */
OO.ui.LookupElement.prototype.abortLookupRequest = function () {
	var oldRequest = this.lookupRequest;
	if ( oldRequest ) {
		// First unset this.lookupRequest to the fail handler will notice
		// that the request is no longer current
		this.lookupRequest = null;
		this.lookupQuery = null;
		oldRequest.abort();
	}
};

/**
 * Get a new request object of the current lookup query value.
 *
 * @protected
 * @abstract
 * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
 */
OO.ui.LookupElement.prototype.getLookupRequest = function () {
	// Stub, implemented in subclass
	return null;
};

/**
 * Pre-process data returned by the request from #getLookupRequest.
 *
 * The return value of this function will be cached, and any further queries for the given value
 * will use the cache rather than doing API requests.
 *
 * @protected
 * @abstract
 * @param {Mixed} response Response from server
 * @return {Mixed} Cached result data
 */
OO.ui.LookupElement.prototype.getLookupCacheDataFromResponse = function () {
	// Stub, implemented in subclass
	return [];
};

/**
 * Get a list of menu option widgets from the (possibly cached) data returned by
 * #getLookupCacheDataFromResponse.
 *
 * @protected
 * @abstract
 * @param {Mixed} data Cached result data, usually an array
 * @return {OO.ui.MenuOptionWidget[]} Menu items
 */
OO.ui.LookupElement.prototype.getLookupMenuOptionsFromData = function () {
	// Stub, implemented in subclass
	return [];
};
<|endoftext|>


<commit_before>var mongodb = require('mongodb');
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;

BookProvider = function(uri) {
  this.db;
  console.log("uri: " + uri);
  mongodb.MongoClient.connect(uri, { server: { auto_reconnect: true } }, function (error, database) {
    if( error ) callback(error);
    else this.db = database;
    //db.open(function(){});
  });
  //this.db= new Db('books', new Server(host, port, {safe: false}, {auto_reconnect: true}, {}));
  //this.db.open(function(){});
};

//initialize info values
BookProvider.prototype.init = function(info, callback) {
  this.getCollection(function(error, book_collection) {
    if ( error ) callback(error);
    else book_collection.insert({info:info});
  });
};

BookProvider.prototype.getCollection= function(callback) {
  this.db.collection('bks', function(error, book_collection) {
    if( error ) callback(error);
    else callback(null, book_collection);
  });
};

//find all books
BookProvider.prototype.findAll = function(callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error);
      else {
        book_collection.find().toArray(function(error, results) {
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};

//find recently edited books
BookProvider.prototype.findRecentEdits = function(callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error);
      else {
        book_collection.find().sort({last_edit:-1}).limit(8).toArray(function(error, results) {
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};
//find recently edited books
BookProvider.prototype.findRecentLogs = function(callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error);
      else {
        book_collection.find().sort({created_at:-1}).limit(8).toArray(function(error, results) {
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};

//find subset of books with tag
BookProvider.prototype.findTag = function(tag, callback) {
  this.getCollection(function(error, book_collection) {
    if(error) callback(error);
    else {
      book_collection.find({tags:tag}).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
    }
  });
};

//search all books
BookProvider.prototype.search = function(query, callback) {
  var name = query._field;
  var value = query._query;
  var dbQuery = {};
  dbQuery[name] = {$regex:value, $options:"-i"};
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

//search books and return subset based on a feild and query (_ref)
BookProvider.prototype.searchRefs = function(query, callback) {
  var name = query._field;
  var value = query._ref;
  var dbQuery = {};
  if (query._refs != undefined) {
    var exclude = query._refs;
    if (exclude.constructor == Array) {
      for (var i = 0; i < exclude.length; i++) {
        var obj = new ObjectID(exclude[i]);
        exclude[i] = obj;
      }
      dbQuery = {_id:{$nin:exclude}};
    } else {
      exclude = new ObjectID(exclude);
      dbQuery = {_id:{$ne:exclude}};
    }
  }
  dbQuery[name] = {$regex:value, $options:"-i"};
  console.log(JSON.stringify(dbQuery));
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

//search books and return subset based on a feild and query (_ref)
BookProvider.prototype.browseRefs = function(query, callback) {
  var dbQuery = {};
  var f = query._field;
  var q = query[query._field];
  if (f == "_fields") {
      dbQuery[query[query._field]] = {$exists:true};
  } else if (query._field == "_types") {
    dbQuery = {type:q};
  } else if (f == "_tags") {
    dbQuery = {tags:{$in:[q]}};
  }
  if (query._refs != undefined) {
    var exclude = query._refs;
    if (exclude.constructor == Array) {
      for (var i = 0; i < exclude.length; i++) {
        var obj = new ObjectID(exclude[i]);
        exclude[i] = obj;
      }
      dbQuery = {_id:{$nin:exclude}};
    } else {
      exclude = new ObjectID(exclude);
      dbQuery = {_id:{$ne:exclude}};
    }
  }
  console.log(dbQuery);
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};
//search books and return subset based on a feild and query (_ref)
BookProvider.prototype.browse = function(query, callback) {
  var dbQuery = {};
  var f = query._field;
  var q = query[query._field];
  if (f == "_fields") {
      dbQuery[query[query._field]] = {$exists:true};
  } else if (query._field == "_types") {
    dbQuery = {type:q};
  } else if (f == "_tags") {
    dbQuery = {tags:{$in:[q]}};
  }
  console.log(dbQuery);
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

// get references from single book to display query from id
BookProvider.prototype.getRefs = function(refs, callback) {
  var query = {_id:{$in:[]}};
  for (var i = 0; i < refs.length; i++) {
    var objId = new ObjectID(refs[i].refId);
    query._id["$in"].push(objId);
  }
  this.getCollection(function(error, book_collection) {
      book_collection.find(query).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

//save new book
BookProvider.prototype.save = function(books, callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error)
      else {
        if( typeof(books.length)=="undefined")
          books = [books];

        for( var i =0;i< books.length;i++ ) {
          book = books[i];
          book.created_at = new Date();
        }

        book_collection.insert(books, function() {
          callback(null, books);
        });
      }
    });
};

BookProvider.prototype.addType = function(type, callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
      book_collection.update({info:{$exists:true}}, {$addToSet:{"info._types":type}});
    }
  });
};

// find a book by id
BookProvider.prototype.findById = function(id, callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
      book_collection.findOne({info:{$exists:true}}, function(error, info) {
          book_collection.findOne({_id:book_collection.db.bson_serializer.ObjectID.createFromHexString(id)}, function(error, result) {
            if (error) callback(error);
            else callback(null, result, info);
        });
      });
    }
  });
};

// get info
BookProvider.prototype.getInfo = function(callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
      book_collection.findOne({info:{$exists:true}}, function(error, info) {
          if (error) callback(error);
          else callback(null, info);
      });
    }
  });
};

// add a tag
BookProvider.prototype.addTag = function(query, set) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else book_collection.update(query, set);
  });
};

// referenced by add note
BookProvider.prototype.addReferencedBy = function(refId, id, title, note) {
  var query = {_id:new ObjectID(refId)};
  var set = { referencedBy: {}};
  set["referencedBy"] = {id:id, note:note, title:title};
  this.getCollection(function(error, book_collection){
    if (error) callback(error);
    else {
      book_collection.update(query, {$addToSet:set});
    }
  });
};
// edit referenced by
BookProvider.prototype.editReferencedBy = function(refId, id, title, note) {
  var query = {_id:new ObjectID(refId), referencedBy:{$elemMatch:{id:id}}};
  var set = {};
  set["referencedBy.$.note"] = note;
  this.getCollection(function(error, book_collection){
    if (error) callback(error);
    else {
      book_collection.update(query, {$set:set});
    }
  });
};
// edit reference
BookProvider.prototype.editRef = function(refId, id, title, note) {
  var query = {_id:new ObjectID(refId), refs:{$elemMatch:{id:id}}};
  var set = {};
  set["refs.$.note"] = note;
  this.getCollection(function(error, book_collection){
    if (error) callback(error);
    else {
      book_collection.update(query, {$set:set});
    }
  });
};

// update a book
BookProvider.prototype.update = function(bookId, books, callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
       book_collection.update(
          {_id:book_collection.db.bson_serializer.ObjectID.createFromHexString(bookId)},
          books,
          function(error, books) {
            if (error) callback(error);
            else callback(null, books);
          });
    }
  });
};

//delete book
BookProvider.prototype.delete = function(bookId, callback) {
        this.getCollection(function(error, book_collection) {
                if(error) callback(error);
                else {
                        book_collection.remove(
                                {_id: book_collection.db.bson_serializer.ObjectID.createFromHexString(bookId)},
                                function(error, book){
                                        if(error) callback(error);
                                        else callback(null, book)
                                });
                        }
        });
};

exports.BookProvider = BookProvider;<commit_msg>.open<commit_after>var mongodb = require('mongodb');
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;

BookProvider = function(uri) {
  this.db = mongodb.MongoClient.connect(uri, { server: { auto_reconnect: true } }, {});
   // if( error ) callback(error);
    //else mongodb = database;
    //db.open(function(){});
  });
  //this.db= new Db('books', new Server(host, port, {safe: false}, {auto_reconnect: true}, {}));
  //this.db.open(function(){});
};

//initialize info values
BookProvider.prototype.init = function(info, callback) {
  this.getCollection(function(error, book_collection) {
    if ( error ) callback(error);
    else book_collection.insert({info:info});
  });
};

BookProvider.prototype.getCollection= function(callback) {
  this.db.collection('bks', function(error, book_collection) {
    if( error ) callback(error);
    else callback(null, book_collection);
  });
};

//find all books
BookProvider.prototype.findAll = function(callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error);
      else {
        book_collection.find().toArray(function(error, results) {
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};

//find recently edited books
BookProvider.prototype.findRecentEdits = function(callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error);
      else {
        book_collection.find().sort({last_edit:-1}).limit(8).toArray(function(error, results) {
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};
//find recently edited books
BookProvider.prototype.findRecentLogs = function(callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error);
      else {
        book_collection.find().sort({created_at:-1}).limit(8).toArray(function(error, results) {
          if( error ) callback(error);
          else callback(null, results);
        });
      }
    });
};

//find subset of books with tag
BookProvider.prototype.findTag = function(tag, callback) {
  this.getCollection(function(error, book_collection) {
    if(error) callback(error);
    else {
      book_collection.find({tags:tag}).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
    }
  });
};

//search all books
BookProvider.prototype.search = function(query, callback) {
  var name = query._field;
  var value = query._query;
  var dbQuery = {};
  dbQuery[name] = {$regex:value, $options:"-i"};
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

//search books and return subset based on a feild and query (_ref)
BookProvider.prototype.searchRefs = function(query, callback) {
  var name = query._field;
  var value = query._ref;
  var dbQuery = {};
  if (query._refs != undefined) {
    var exclude = query._refs;
    if (exclude.constructor == Array) {
      for (var i = 0; i < exclude.length; i++) {
        var obj = new ObjectID(exclude[i]);
        exclude[i] = obj;
      }
      dbQuery = {_id:{$nin:exclude}};
    } else {
      exclude = new ObjectID(exclude);
      dbQuery = {_id:{$ne:exclude}};
    }
  }
  dbQuery[name] = {$regex:value, $options:"-i"};
  console.log(JSON.stringify(dbQuery));
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

//search books and return subset based on a feild and query (_ref)
BookProvider.prototype.browseRefs = function(query, callback) {
  var dbQuery = {};
  var f = query._field;
  var q = query[query._field];
  if (f == "_fields") {
      dbQuery[query[query._field]] = {$exists:true};
  } else if (query._field == "_types") {
    dbQuery = {type:q};
  } else if (f == "_tags") {
    dbQuery = {tags:{$in:[q]}};
  }
  if (query._refs != undefined) {
    var exclude = query._refs;
    if (exclude.constructor == Array) {
      for (var i = 0; i < exclude.length; i++) {
        var obj = new ObjectID(exclude[i]);
        exclude[i] = obj;
      }
      dbQuery = {_id:{$nin:exclude}};
    } else {
      exclude = new ObjectID(exclude);
      dbQuery = {_id:{$ne:exclude}};
    }
  }
  console.log(dbQuery);
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};
//search books and return subset based on a feild and query (_ref)
BookProvider.prototype.browse = function(query, callback) {
  var dbQuery = {};
  var f = query._field;
  var q = query[query._field];
  if (f == "_fields") {
      dbQuery[query[query._field]] = {$exists:true};
  } else if (query._field == "_types") {
    dbQuery = {type:q};
  } else if (f == "_tags") {
    dbQuery = {tags:{$in:[q]}};
  }
  console.log(dbQuery);
  this.getCollection(function(error, book_collection) {
      book_collection.find(dbQuery).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

// get references from single book to display query from id
BookProvider.prototype.getRefs = function(refs, callback) {
  var query = {_id:{$in:[]}};
  for (var i = 0; i < refs.length; i++) {
    var objId = new ObjectID(refs[i].refId);
    query._id["$in"].push(objId);
  }
  this.getCollection(function(error, book_collection) {
      book_collection.find(query).toArray(function(error, results){
        if (error) callback(error);
        else callback(null, results);
      });
  });
};

//save new book
BookProvider.prototype.save = function(books, callback) {
    this.getCollection(function(error, book_collection) {
      if( error ) callback(error)
      else {
        if( typeof(books.length)=="undefined")
          books = [books];

        for( var i =0;i< books.length;i++ ) {
          book = books[i];
          book.created_at = new Date();
        }

        book_collection.insert(books, function() {
          callback(null, books);
        });
      }
    });
};

BookProvider.prototype.addType = function(type, callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
      book_collection.update({info:{$exists:true}}, {$addToSet:{"info._types":type}});
    }
  });
};

// find a book by id
BookProvider.prototype.findById = function(id, callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
      book_collection.findOne({info:{$exists:true}}, function(error, info) {
          book_collection.findOne({_id:book_collection.db.bson_serializer.ObjectID.createFromHexString(id)}, function(error, result) {
            if (error) callback(error);
            else callback(null, result, info);
        });
      });
    }
  });
};

// get info
BookProvider.prototype.getInfo = function(callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
      book_collection.findOne({info:{$exists:true}}, function(error, info) {
          if (error) callback(error);
          else callback(null, info);
      });
    }
  });
};

// add a tag
BookProvider.prototype.addTag = function(query, set) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else book_collection.update(query, set);
  });
};

// referenced by add note
BookProvider.prototype.addReferencedBy = function(refId, id, title, note) {
  var query = {_id:new ObjectID(refId)};
  var set = { referencedBy: {}};
  set["referencedBy"] = {id:id, note:note, title:title};
  this.getCollection(function(error, book_collection){
    if (error) callback(error);
    else {
      book_collection.update(query, {$addToSet:set});
    }
  });
};
// edit referenced by
BookProvider.prototype.editReferencedBy = function(refId, id, title, note) {
  var query = {_id:new ObjectID(refId), referencedBy:{$elemMatch:{id:id}}};
  var set = {};
  set["referencedBy.$.note"] = note;
  this.getCollection(function(error, book_collection){
    if (error) callback(error);
    else {
      book_collection.update(query, {$set:set});
    }
  });
};
// edit reference
BookProvider.prototype.editRef = function(refId, id, title, note) {
  var query = {_id:new ObjectID(refId), refs:{$elemMatch:{id:id}}};
  var set = {};
  set["refs.$.note"] = note;
  this.getCollection(function(error, book_collection){
    if (error) callback(error);
    else {
      book_collection.update(query, {$set:set});
    }
  });
};

// update a book
BookProvider.prototype.update = function(bookId, books, callback) {
  this.getCollection(function(error, book_collection) {
    if (error) callback(error);
    else {
       book_collection.update(
          {_id:book_collection.db.bson_serializer.ObjectID.createFromHexString(bookId)},
          books,
          function(error, books) {
            if (error) callback(error);
            else callback(null, books);
          });
    }
  });
};

//delete book
BookProvider.prototype.delete = function(bookId, callback) {
        this.getCollection(function(error, book_collection) {
                if(error) callback(error);
                else {
                        book_collection.remove(
                                {_id: book_collection.db.bson_serializer.ObjectID.createFromHexString(bookId)},
                                function(error, book){
                                        if(error) callback(error);
                                        else callback(null, book)
                                });
                        }
        });
};

exports.BookProvider = BookProvider;<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  ListView,
  Image,
  ToastAndroid,
  Dimensions,
  TouchableNativeFeedback,
  RefreshControl,
  InteractionManager,
  ToolbarAndroid,
} from 'react-native';

import { connect } from 'react-redux';
import { getTopicList } from '../../actions/community.js';
import { standardColor, nodeColor, idColor  } from '../../config/config';

import CommunityTopic from '../../components/CommunityTopic';

import { getTopicURL, fetchMessages } from '../../dao/dao';
import moment from '../../utils/moment';

const ds = new ListView.DataSource({
  rowHasChanged: (row1, row2) => row1 !== row2,
});

let toolbarActions = [];

class Message extends Component {
    constructor(props){
        super(props);
        this.state = {
            messages: [],
        }
    }

  onNavClicked = (rowData) => {
    const { navigator } = this.props;
    if (navigator) {
      navigator.pop();
    }
  }

  _renderRow = (rowData,
    sectionID: number | string,
    rowID: number | string,
    highlightRow: (sectionID: number, rowID: number) => void
  ) => {

    let uri;
    if (rowData.profilepicture == '') {
      let path = rowData.avatar.toString().replace('\\', '');
      uri = `http://photo.d7vg.com/avatar/${path}.png@50w.png`;
    } else {
      uri = `http://photo.d7vg.com/avaself/${rowData.psnid}.png@50w.png`;
    }
    let time = parseInt(rowData.date);
    time *= 1000;
    let date = new Date(time);
    let fromNow = moment(date).fromNow();

    let TouchableElement = TouchableNativeFeedback;

    return (
      <View rowID={ rowID } style={{
            marginTop: 7,
            backgroundColor: '#FFFFFF',
            elevation: 1,
        }}>
        <TouchableElement
          delayPressIn={0}
          background={TouchableNativeFeedback.SelectableBackgroundBorderless()}
          >
          <View style={{ flex: 1, flexDirection: 'row',  padding: 12 }}>
            <Image
              source={{ uri: uri }}
              style={styles.avatar}
              />

            <View style={{ marginLeft: 10, flex: 1, flexDirection: 'column'}}>
              <Text
                ellipsizeMode={'tail'}
                numberOfLines={3}
                style={{ flex: 2.5,color: 'black', }}>
                {rowData.content}
              </Text>

              <View style={{ flex: 1.1, flexDirection: 'row', justifyContent :'space-between' }}>
                <Text style={{ flex: -1, color: idColor,textAlign : 'center', textAlignVertical: 'center' }}>{rowData.psnid}</Text>
                <Text style={{ flex: -1,textAlign : 'center', textAlignVertical: 'center' }}>{fromNow}</Text>
              </View>

            </View>

          </View>
        </TouchableElement>
      </View>
    )
  }

  componentWillMount = async () => {
      this.fetchMessages();
  }

  async fetchMessages () {
    const data = await fetchMessages(this.props.psnid);
    console.log(data);
    this.setState({
        messages: data.data,
    });
  }

  render(){
    // console.log('Message.js rendered');
    return (
          <View
            style={{flex:1}}
            onStartShouldSetResponder={() => false}
            onMoveShouldSetResponder={() => false}
            >
              <ToolbarAndroid
                navIcon={require('image!ic_back_white')}
                overflowIcon={require('image!ic_more_white')}
                title={'我的消息'}
                style={styles.toolbar}
                actions={toolbarActions}
                onIconClicked={this.onNavClicked}
              />
              <ListView
                refreshControl={
                  <RefreshControl
                    refreshing={this.state.messages.length == 0 ? true : false}
                    colors={[standardColor]}
                    />
                }
                pageSize = {32}
                removeClippedSubviews={false}
                enableEmptySections={true}
                dataSource={ ds.cloneWithRows(this.state.messages) }
                renderRow={this._renderRow}
                />
         </View>
    )
  }

}

const styles = StyleSheet.create({
  avatar: {
    width: 50,
    height: 50,
  },
  toolbar: {
    backgroundColor: standardColor,
    height: 56,
    elevation: 4,
  },
});

// function mapStateToProps(state) {
//     return {
//       community: state.community,
//     };
// }

// export default connect(
//   mapStateToProps
// )(Community);

export default Message;
<commit_msg>添加消息跳转<commit_after>import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  ListView,
  Image,
  ToastAndroid,
  Dimensions,
  TouchableNativeFeedback,
  RefreshControl,
  InteractionManager,
  ToolbarAndroid,
} from 'react-native';

import { connect } from 'react-redux';
import { getTopicList } from '../../actions/community.js';
import { standardColor, nodeColor, idColor  } from '../../config/config';

import CommunityTopic from '../../components/CommunityTopic';

import { getTopicURL, fetchMessages,  } from '../../dao/dao';
import moment from '../../utils/moment';

const ds = new ListView.DataSource({
  rowHasChanged: (row1, row2) => row1 !== row2,
});

let toolbarActions = [];

class Message extends Component {
    constructor(props){
        super(props);
        this.state = {
            messages: [],
        }
    }

  onNavClicked = (rowData) => {
    const { navigator } = this.props;
    if (navigator) {
      navigator.pop();
    }
  }

  _pressRow = (rowData) => {
    const { navigator } = this.props;
    let URL;
    let type = rowData.type;
    switch (type) {
      case 'topic':
        URL = getTopicURL(rowData.tid);
        navigator.push({
          component: CommunityTopic,
          params: {
            URL,
            title: rowData.content,
            rowData
          }
        });
        break;
      case 'gene':
        URL = getGeneURL(rowData.tid);
        navigator.push({
          component: GeneTopic,
          params: {
            URL,
            title: rowData.content,
            rowData
          }
        });
        break;
      default:
        ToastAndroid.show(`type '${rowData.type}' not implement yet.`,2000);
        break;
    }
  }

  _renderRow = (rowData,
    sectionID: number | string,
    rowID: number | string,
    highlightRow: (sectionID: number, rowID: number) => void
  ) => {

    let uri;
    if (rowData.profilepicture == '') {
      let path = rowData.avatar.toString().replace('\\', '');
      uri = `http://photo.d7vg.com/avatar/${path}.png@50w.png`;
    } else {
      uri = `http://photo.d7vg.com/avaself/${rowData.psnid}.png@50w.png`;
    }
    let time = parseInt(rowData.date);
    time *= 1000;
    let date = new Date(time);
    let fromNow = moment(date).fromNow();

    let TouchableElement = TouchableNativeFeedback;

    return (
      <View rowID={ rowID } style={{
            marginTop: 7,
            backgroundColor: '#FFFFFF',
            elevation: 1,
        }}>
        <TouchableElement
          delayPressIn={0}
          background={TouchableNativeFeedback.SelectableBackgroundBorderless()}
          onPress={()=>this._pressRow(rowData)}
          >
          <View style={{ flex: 1, flexDirection: 'row',  padding: 12 }}>
            <Image
              source={{ uri: uri }}
              style={styles.avatar}
              />

            <View style={{ marginLeft: 10, flex: 1, flexDirection: 'column'}}>
              <Text
                ellipsizeMode={'tail'}
                numberOfLines={3}
                style={{ flex: 2.5,color: 'black', }}>
                {rowData.content}
              </Text>

              <View style={{ flex: 1.1, flexDirection: 'row', justifyContent :'space-between' }}>
                <Text style={{ flex: -1, color: idColor,textAlign : 'center', textAlignVertical: 'center' }}>{rowData.psnid}</Text>
                <Text style={{ flex: -1,textAlign : 'center', textAlignVertical: 'center' }}>{fromNow}</Text>
              </View>

            </View>

          </View>
        </TouchableElement>
      </View>
    )
  }

  componentWillMount = async () => {
      this.fetchMessages();
  }

  async fetchMessages () {
    const data = await fetchMessages(this.props.psnid);
    this.setState({
        messages: data.data,
    });
  }

  render(){
    // console.log('Message.js rendered');
    return (
          <View
            style={{flex:1}}
            onStartShouldSetResponder={() => false}
            onMoveShouldSetResponder={() => false}
            >
              <ToolbarAndroid
                navIcon={require('image!ic_back_white')}
                overflowIcon={require('image!ic_more_white')}
                title={'我的消息'}
                style={styles.toolbar}
                actions={toolbarActions}
                onIconClicked={this.onNavClicked}
              />
              <ListView
                refreshControl={
                  <RefreshControl
                    refreshing={this.state.messages.length == 0 ? true : false}
                    colors={[standardColor]}
                    />
                }
                pageSize = {32}
                removeClippedSubviews={false}
                enableEmptySections={true}
                dataSource={ ds.cloneWithRows(this.state.messages) }
                renderRow={this._renderRow}
                />
         </View>
    )
  }

}

const styles = StyleSheet.create({
  avatar: {
    width: 50,
    height: 50,
  },
  toolbar: {
    backgroundColor: standardColor,
    height: 56,
    elevation: 4,
  },
});

// function mapStateToProps(state) {
//     return {
//       community: state.community,
//     };
// }

// export default connect(
//   mapStateToProps
// )(Community);

export default Message;
<|endoftext|>


<commit_before>var expect = require('expect.js');
var request = require('request');

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

describe('PUT singular', function () {
  before(fixtures.vegetable.init);
  beforeEach(fixtures.vegetable.create);
  after(fixtures.vegetable.deinit);

  it("should replace the addressed object if it exists", function (done) {
    var radicchio = vegetables[7];
    var options = {
      url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
      json: true
    };
    request.get(options, function (err, response, body) {
      if (err) return done(err);
      expect(response).to.have.property('statusCode', 200);
      expect(body).to.have.property('name', 'Radicchio');

      // put the leek on the server
      var options = {
        url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
        json: {
          name: 'Leek'
        }
      };
      request.put(options, function (err, response, body) {
        if (err) return done(err);
        expect(response).to.have.property('statusCode', 200);

        // check it's not Radicchio
        var options = {
          url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
          json: true
        };
        request.get(options, function (err, response, body) {
          if (err) return done(err);
          expect(response).to.have.property('statusCode', 200);
          expect(body).to.have.property('name', 'Leek');
          done();
        });
      });
    });
  });

  it('should create the addressed document if non-existant', function (done) {
    var id = 'badbadbadbadbadbadbadbad';
    var options = {
      url: 'http://localhost:8012/api/v1/vegetables/' + id,
      json: true
    };
    // first check it's not there
    request.get(options, function (err, response, body) {
      if (err) return done(err);
      expect(response).to.have.property('statusCode', 404);

      // put it on server
      var options = {
        url: 'http://localhost:8012/api/v1/vegetables/' + id,
        json: { name: 'Cucumber' }
      };
      request.put(options, function (err, response, body) {
        if (err) return done(err);
        expect(response).to.have.property('statusCode', 200);

        // check it's there
        var options = {
          url: 'http://localhost:8012/api/v1/vegetables/' + id,
          json: true
        };
        request.get(options, function (err, response, body) {
          expect(response).to.have.property('statusCode', 200);
          expect(body).to.have.property('_id', id);
          expect(body).to.have.property('name', 'Cucumber');
          done();
        });
      });
    });
  });

  it('should fire pre save Mongoose middleware', function (done) {
    fixtures.vegetable.preCount = 0;

    // put it on server
    var id = 'adaadaadaadaadaadaadaada';
    var options = {
      url: 'http://localhost:8012/api/v1/vegetables/' + id,
      json: { name: 'Micro Turnip Greens' }
    };
    request.put(options, function (error, response, body) {
      if (error) return done(error);

      expect(fixtures.vegetable.preCount).to.be(1);
      done();
    });
  });

});

<commit_msg>Disallow creating docs with PUT<commit_after>var expect = require('expect.js');
var request = require('request');

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

describe('PUT singular', function () {
  before(fixtures.vegetable.init);
  beforeEach(fixtures.vegetable.create);
  after(fixtures.vegetable.deinit);

  it("should replace the addressed object if it exists", function (done) {
    var radicchio = vegetables[7];
    var options = {
      url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
      json: true
    };
    request.get(options, function (err, response, body) {
      if (err) return done(err);
      expect(response).to.have.property('statusCode', 200);
      expect(body).to.have.property('name', 'Radicchio');

      // put the leek on the server
      var options = {
        url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
        json: {
          name: 'Leek'
        }
      };
      request.put(options, function (err, response, body) {
        if (err) return done(err);
        expect(response).to.have.property('statusCode', 200);

        // check it's not Radicchio
        var options = {
          url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
          json: true
        };
        request.get(options, function (err, response, body) {
          if (err) return done(err);
          expect(response).to.have.property('statusCode', 200);
          expect(body).to.have.property('name', 'Leek');
          done();
        });
      });
    });
  });

  it('should only allow updates', function (done) {
    var id = 'badbadbadbadbadbadbadbad';
    var options = {
      url: 'http://localhost:8012/api/v1/vegetables/' + id,
      json: true
    };
    // First check it's not there
    request.get(options, function (err, response, body) {
      if (err) return done(err);
      expect(response).to.have.property('statusCode', 404);

      // Attempt to update non-existant doc
      var options = {
        url: 'http://localhost:8012/api/v1/vegetables/' + id,
        json: { name: 'Cucumber' }
      };
      request.put(options, function (err, response, body) {
        if (err) return done(err);
        expect(response).to.have.property('statusCode', 500);

        // Make sure it wasn't created
        var options = {
          url: 'http://localhost:8012/api/v1/vegetables/' + id,
          json: true
        };
        request.get(options, function (err, response, body) {
          if (err) return done(err);
          expect(response).to.have.property('statusCode', 404);
          done();
        });
      });
    });
  });

  it('should fire pre save Mongoose middleware', function (done) {
    fixtures.vegetable.preCount = 0;

    var radicchio = vegetables[7];
    var options = {
      url: 'http://localhost:8012/api/v1/vegetables/' + radicchio._id,
      json: { name: 'Radicchio di Treviso' }
    };
    request.put(options, function (error, response, body) {
      if (error) return done(error);

      expect(fixtures.vegetable.preCount).to.be(1);
      done();
    });
  });

});

<|endoftext|>


<commit_before>angular.module('angupoly', [])
.directive('angupoly', function($rootScope, $parse) {
  return {
    priority : 42,
    restrict : 'A',
    compile  : function(tElement, tAttr) {
      var conf = $rootScope.$eval(tAttr.angupoly);
      var assignables = {};
      var paths       = {};
      var needsMutationObserver;

      Object.keys(conf).forEach(function(attrName) {
        var path  = conf[attrName],
            parse = $parse(path);
        if (parse.assign) {
          assignables[attrName] = parse.assign;
          needsMutationObserver = true;
        }
        paths[attrName] = path;
      });
      return function(scope, element) {
        var el = element[0];

        // from angular scope to attribute
        // http://www.polymer-project.org/platform/node_bind.html
        for (var attrName in paths) {
          el.bind(attrName, new PathObserver(scope, paths[attrName]));
        }

        if (needsMutationObserver) {
          // from attribute to angular scope
          // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
          // http://caniuse.com/mutationobserver
          new MutationObserver(function(mutations) {
            var mutation,
                updates,
                i = 0;
            while ((mutation = mutations[i++])) {
              for (var attrName in assignables) {
                if (mutation.attributeName.indexOf(attrName) !== -1) {
                  assignables[attrName](scope, mutation.target[attrName]);
                  updates = true;
                }
              }
            }
            if (updates) {
              scope.$apply();
            }
          }).observe(el, {attributes:true});
        }
      };
    }
  };
});<commit_msg>mutation.attributeName is a string<commit_after>angular.module('angupoly', [])
.directive('angupoly', function($rootScope, $parse) {
  return {
    priority : 42,
    restrict : 'A',
    compile  : function(tElement, tAttr) {
      var conf = $rootScope.$eval(tAttr.angupoly);
      var assignables = {};
      var paths       = {};
      var needsMutationObserver;

      Object.keys(conf).forEach(function(attrName) {
        var path  = conf[attrName],
            parse = $parse(path);
        if (parse.assign) {
          assignables[attrName] = parse.assign;
          needsMutationObserver = true;
        }
        paths[attrName] = path;
      });
      return function(scope, element) {
        var el = element[0];

        // from angular scope to attribute
        // http://www.polymer-project.org/platform/node_bind.html
        for (var attrName in paths) {
          el.bind(attrName, new PathObserver(scope, paths[attrName]));
        }

        if (needsMutationObserver) {
          // from attribute to angular scope
          // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
          // http://caniuse.com/mutationobserver
          new MutationObserver(function(mutations) {
            var mutation,
                updates,
                i = 0;
            while ((mutation = mutations[i++])) {
              for (var attrName in assignables) {
                if (mutation.attributeName === attrName) {
                  assignables[attrName](scope, mutation.target[attrName]);
                  updates = true;
                }
              }
            }
            if (updates) {
              scope.$apply();
            }
          }).observe(el, {attributes:true});
        }
      };
    }
  };
});<|endoftext|>


<commit_before>(function() {
    var Sock = function() {
        var socket;
        if (!window.WebSocket) {
            window.WebSocket = window.MozWebSocket;
        }

        if (window.WebSocket) {
            socket = new WebSocket("ws://localhost:9000/assets/websocket");
            socket.onopen = onopen;
            socket.onmessage = onmessage;
            socket.onclose = onclose;
        } else {
            alert("Your browser does not support Web Socket.");
        }

        function onopen(event) {
            getTextAreaElement().value = "Web Socket opened!";
        }

        function onmessage(event) {
            appendTextArea(event.data);
        }
        function onclose(event) {
            appendTextArea("Web Socket closed");
        }

        function appendTextArea(newData) {
            var el = getTextAreaElement();
            el.value = el.value + '\n' + newData;
        }

        function getTextAreaElement() {
            return document.getElementById('responseText');
        }

        function send(event) {
            event.preventDefault();
            if (window.WebSocket) {
                if (socket.readyState == WebSocket.OPEN) {
                    var msg = {};
                    msg.message = event.target.message.value;
                    socket.send(JSON.stringify(msg));
                    //socket.send(event.target.message.value);
                } else {
                    alert("The socket is not open.");
                }
            }
        }
        document.forms.inputform.addEventListener('submit', send, false);
    }
    window.addEventListener('load', function() { new Sock(); }, false);
})();
<commit_msg>Remove reference on localhost in the web socket sample<commit_after>(function() {
    var Sock = function() {
        var socket;
        if (!window.WebSocket) {
            window.WebSocket = window.MozWebSocket;
        }

        if (window.WebSocket) {
            // compute url.
            var url = "ws://" + window.location.host + "/assets/websocket";
            socket = new WebSocket(url);
            socket.onopen = onopen;
            socket.onmessage = onmessage;
            socket.onclose = onclose;
        } else {
            alert("Your browser does not support Web Socket.");
        }

        function onopen(event) {
            getTextAreaElement().value = "Web Socket opened!";
        }

        function onmessage(event) {
            appendTextArea(event.data);
        }
        function onclose(event) {
            appendTextArea("Web Socket closed");
        }

        function appendTextArea(newData) {
            var el = getTextAreaElement();
            el.value = el.value + '\n' + newData;
        }

        function getTextAreaElement() {
            return document.getElementById('responseText');
        }

        function send(event) {
            event.preventDefault();
            if (window.WebSocket) {
                if (socket.readyState == WebSocket.OPEN) {
                    var msg = {};
                    msg.message = event.target.message.value;
                    socket.send(JSON.stringify(msg));
                    //socket.send(event.target.message.value);
                } else {
                    alert("The socket is not open.");
                }
            }
        }
        document.forms.inputform.addEventListener('submit', send, false);
    }
    window.addEventListener('load', function() { new Sock(); }, false);
})();
<|endoftext|>


<commit_before>var loaders = require("./loaders");
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    devtool: 'source-map',
    entry: {
        'dist/jsonforms': './src/bootstrap/jsonforms_bootstrap.ts',
        'examples/assets/jsonforms': './src/bootstrap/jsonforms_bootstrap.ts'
    },
    output: {
        filename: '[name].js',
        path: './'
    },
    resolve: {
        root: __dirname,
        extensions: ['', '.ts', '.js', '.json']
    },
    resolveLoader: {
        modulesDirectories: ["node_modules"]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin(
            {
                warning: false,
                mangle: true,
                comments: false
            }
        ),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery',
            'window.jquery': 'jquery'
        }),
        new CopyWebpackPlugin([
            {
                from: 'src/bootstrap/jsonforms-bootstrap.css',
                to:   'dist/jsonforms.css'
            },
            {
                from: 'src/bootstrap/jsonforms-bootstrap.css',
                to:   'examples/assets/jsonforms.css'
            },
        ])
    ],
    module: {
        preLoaders: [
            {
                test: /\.ts$/,
                loader: "tslint"
            }
        ],
        loaders: loaders
    },
    tslint: {
        // These options are useful if you want to save output to files
        // for your continuous integration server
        fileOutput: {
            // The directory where each file's report is saved
            dir: "./reports/tslint",

            // The extension to use for each report's filename. Defaults to "txt"
            ext: "xml",

            // If true, all files are removed from the report directory at the beginning of run
            clean: true,

            // A string to include at the top of every report file.
            // Useful for some report formats.
            header: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<checkstyle version=\"5.7\">",

            // A string to include at the bottom of every report file.
            // Useful for some report formats.
            footer: "</checkstyle>"
        }
    }
};
<commit_msg>Some adaption in the bootstrap build for publishing<commit_after>var loaders = require("./loaders");
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    devtool: 'source-map',
    entry: {
        'dist/jsonforms-bootstrap': './src/bootstrap/jsonforms_bootstrap.ts',
        'examples/assets/jsonforms': './src/bootstrap/jsonforms_bootstrap.ts'
    },
    output: {
        filename: '[name].js',
        path: './'
    },
    resolve: {
        root: __dirname,
        extensions: ['', '.ts', '.js', '.json']
    },
    resolveLoader: {
        modulesDirectories: ["node_modules"]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin(
            {
                warning: false,
                mangle: true,
                comments: false
            }
        ),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery',
            'window.jquery': 'jquery'
        }),
        new CopyWebpackPlugin([
            {
                from: 'src/bootstrap/jsonforms-bootstrap.css',
                to:   'dist/jsonforms-bootstrap.css'
            },
            {
                from: 'src/bootstrap/jsonforms-bootstrap.css',
                to:   'examples/assets/jsonforms.css'
            }
        ])
    ],
    module: {
        preLoaders: [
            {
                test: /\.ts$/,
                loader: "tslint"
            }
        ],
        loaders: loaders
    },
    tslint: {
        // These options are useful if you want to save output to files
        // for your continuous integration server
        fileOutput: {
            // The directory where each file's report is saved
            dir: "./reports/tslint",

            // The extension to use for each report's filename. Defaults to "txt"
            ext: "xml",

            // If true, all files are removed from the report directory at the beginning of run
            clean: true,

            // A string to include at the top of every report file.
            // Useful for some report formats.
            header: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<checkstyle version=\"5.7\">",

            // A string to include at the bottom of every report file.
            // Useful for some report formats.
            footer: "</checkstyle>"
        }
    }
};
<|endoftext|>


<commit_before>
<commit_msg>Add Content Demoing a webpack Config<commit_after>const path = require('path');
const webpack = require('webpack');

/*
 * webpack accepts configuration files written in multiple programming and data
 * languages.  Besides JavaScript, TypeScript is supported, among others.  See
 * https://webpack.js.org/configuration/configuration-languages/ for details.
 * This template, however, will demonstrate a webpack configuration using Node
 * compatible JavaScript.
 */

/*
 * The base directory, an absolute path, for resolving entry points and loaders
 * from configuration.  By default the current directory is used, but it's
 * recommended to pass a value in your configuration.  This makes your
 * configuration independent from CWD (current working directory).
 *
 * @param {string=} - context
 */
const context = path.resolve(__dirname, 'src');

/*
 * The point or points to enter the application.  If an array is passed, all
 * items will be executed.
 *
 * A dynamically loaded module is not an entry point.
 *
 * Simple rule: one entry point per HTML page.  SPA: one entry point.  MPA:
 * multiple entry points.
 *
 * @param {string} entry - The single entry point.  It will have a chunk name of
 *   `'main'`.
 * @param {string[]} entry - TODO: Determine what happens in this case.
 * @param {!Object<string|string[]>} entry - An object defining multiple
 *   entrypoints.  Each key is the name of each chunk.
 * @param {function (): string|string[]|!Object<string|string[]>} entry - A
 *   function to be called to return the entrypoint(s).  Necessary when the value
 *   is dynamic.
 */
const entry = './index.js';
const entry = {
  main: './index.js',
  chunk1: './chunk1-entry.js',
  chunk2: './chunk2-entry.js',
};

/*
 * This option provides a way of excluding dependencies from the output bundles.
 * Instead, the created bundle relies on that dependency to be present in the
 * consumer's environment.
 *
 * That is, this prevents bundling of certain imported packages.
 *
 * See https://webpack.js.org/configuration/externals/ for details.
 *
 * @param {!Object<string|!Array|!Object>|!RegExp|function (context, request, callback)}
 *   - The key should math the name of the imported module.
 */
const externals = undefined;

/*
 * These options determine how the different types of modules within a project will
 * be treated.
 *
 * @param {?Object=}
 */
const loaders = {
  /*
   * Prevent webpack from parsing any files matching the given regular
   * expression(s).
   *
   * @param {!RegExp=|!RegExp[]=|function (string): boolean=}
   */
  noParse: /jquery|lodash/,

  /*
   * An array of rules which are matched to requests when modules are created.
   * These rules can modify how the module is created.  They can apply loaders to
   * the module, or modify the parser.
   *
   * A Rule can be separated into three parts — Conditions, Results, and nested
   * Rules.
   *
   * **Rule Conditions**
   *
   * There are two input values for the conditions:
   *
   * 1. The resource: An absolute path to the file requested.  It's already resolved
   *    according to the resolve rules.
   *
   * 2. The issuer: An absolute path to the file of the module which requested the
   *    resource.  It's the location of the import.
   *
   * In a rule, the properties `test`, `include`, `exclude`, and `resource` are
   * matched with the resource and the property `issuer` is matched with the
   * issuer.
   *
   * When using multiple conditions, all conditions must match.
   *
   * **Rule Results**
   *
   * Rule results are used only when the rule condition matches.
   *
   * There are two output values of a Rule:
   *
   * 1. Applied loaders: An array of loaders applied to the resource.
   *
   * 2. Parser options: An options object which should be used to create the parser
   *    for this module.
   *
   * These properties affect the loaders: `loader`, `options`, `use`.
   *
   * The `enforce` property affects the loader category.  Whether it's a normal,
   * pre- or post- loader.
   *
   * The `parser` property affects the parser options.
   *
   * @param {Rule[]}
   */
  rules: [
    {
      // Conditions:

      /*
       * A Condition in the following documentation is shorthand for the following
       * parameter declarations.
       *
       * @param {string}
       * @param {!RegExp}
       * @param {function (input: string): boolean}
       * @param {Condition[]}
       * @param {{ test: Condition, included: Condition, excluded: Condition, and: Condition, or: Condition, not: Condition}}
       */

      /*
       * Shorthand for `resource.test`.
       *
       * @param {Condition}
       */
      test: undefined,
      /*
       * Shorthand for `resource.include`.
       *
       * @param {Condition}
       */
      include: undefined,
      /*
       * Shorthand for `resource.exclude`.
       *
       * @param {Condition}
       */
      exclude: undefined,

      /*
       * @param {Condition}
       */
      resource: {
        test: undefined,
        include: undefined,
        exclude: undefined,
        and: [],
        or: [],
        not: [],
      },
      /*
       * @param {Condition}
       */
      resourceQuery: undefined,

      /*
       * @param {Condition}
       */
      issuer: undefined,

      // Results:

      /*
       * @param {'pre'|'post'}
       */
      enforce: undefined,

      /*
       * @param {string}
       */
      loader: undefined,
      options: {},

      use: [
        {
          /*
           * @param {string}
           */
          loader: 'foo-loader',
          /*
           * @param {Object=}
           */
          options: {},
        }
      ],

      /*
       * @param {Object=}
       */
      parser: undefined,

      // Nested Rules:

      /*
       * @param {Rule[]=}
       */
      oneOf: undefined,

      /*
       * @param {Rule[]=}
       */
      rules: undefined,
    },
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
      ]
    },
    {
      test: /\.(png|svg|jpg|gif)$/,
      use: [
        'file-loader',
      ]
    },
    // ...
  ],
};

/*
 * The set of options instructing webpack on how and where it should output your
 * bundles, assets, and anything else you bundle or load with webpack.
 *
 * The following only shows the most commonly used options.  See
 * https://webpack.js.org/configuration/output/ for more details.
 *
 * @param {!Object}
 */
const output = {
  /*
   * The name of each output bundle.  The bundle is written to the directory
   * specified by the `output.path` option.
   *
   * For a single entry point, this may be a static name, that is, does not contain
   * substitutions.
   *
   * However, when creating multiple bundles via more than one entry point, code
   * splitting, or various plugins, you should use one of the following
   * substitutions to give each bundle a unique name.
   *
   * | Substitution | Description |
   * | :----------: | ----------- |
   * | [chunkhash]  | The hash of the chunk content. |
   * | [file]       | The module filename. |
   * | [filebase]   | The module basename. |
   * | [hash]       | The hash of the module identifier. |
   * | [id]         | The module identifier. |
   * | [name]       | The module name. |
   * | [query]      | The module query, i.e., the string following `?` in the filename. |
   *
   * Note this option is called `filename` but you are still allowed to use
   * something like `"js/[name]/bundle.js"` to create a folder structure.
   *
   * Note this options does not affect output files for on-demand-loaded chunks.
   * For these files the `output.chunkFilename` option is used.  It also doesn't
   * affect files created by loaders.  For these files see loader options.
   *
   * Make sure to read the [Caching guide][Caching] for details.  There are more
   * steps involved than just setting this option.
   *
   * The lengths of `[hash]` and `[chunkhash]` can be specified using `[hash:#]`
   * (defaults to 20).  Alternatively, specify `output.hashDigestLength` to
   * configure the length globally.
   *
   * When using the `ExtractTextWebpackPlugin`, use `[contenthash]` to obtain a
   * hash of the extracted file (neither `[hash]` nor `[chunkhash]` work).
   *
   * [Caching]: https://webpack.js.org/guides/caching/
   *
   * @param {string}
   */
  filename: 'app.js',

  /*
   * The output directory as an absolute path.
   *
   * Note that `[hash]` in this parameter will be replaced with an hash of the
   * compilation.
   *
   * @param {string=}
   */
  path: path.resolve(__dirname, 'dist'),

  /*
   * Tells webpack to include comments in bundles with information about the
   * contained modules.  This option defaults to `false`.  In production, it
   * should be `false`.
   *
   * @param {boolean=false}
   */
  pathinfo: false,

  /*
   * This option specifies the public URL of the output directory when referenced
   * in a browser.  A relative URL is resolved relative to the HTML page (or <base>
   * tag).  Server-relative URLs, protocol-relative URLs, or absolute URLs are also
   * possible and sometimes required, i.e., when hosting assets on a CDN.
   *
   * The value of this option is prefixed to every URL created by the runtime or
   * loaders.  Because of this, the value of this option ends with `/` in most
   * cases.
   *
   * The default value is an empty string ''.
   *
   * Simple rule: The URL of your `output.path` from the view of the HTML page.
   *
   * @param {string}
   */
  publicPath: 'https://cdn.example.com/assets/',
};

/*
 * These options allows you to control how webpack notifies you of assets and
 * entrypoints that exceed a specific file limit.
 *
 * @param {?Object=}
 */
const performance = {
  /*
   * This option allows webpack to control what files are used to calculate
   * performance hints.
   *
   * @param {function (assetFilename: string): boolean=}
   */
  assetFilter: function (assetFilename) {
    return !(/\.map$/.test(assetFilename))
  },

  /*
   * Turns hints on/off.  In addition, tells webpack to throw either an error or
   * a warning when hints are found.
   *
   * @param {false|'error'|'warning'} - [hints='warning']
   */
  hints: 'error',

  /*
   * The maximum allowable size, in bytes, of an entrypoint before a hint is
   * issued.
   *
   * @param {number=} - [maxEntrypointSize=250000]
   */
  maxEntrypointSize: 250000,

  /*
   * The maximum allowable size, in bytes, of an emitted asset before a hint is
   * issued.
   *
   * @param {number=} - [maxAssetSize=250000]
   */
  maxAssetSize: 250000,
};

/*
 * The plugins option is used to customize the webpack build process in a variety
 * of ways.
 *
 * See https://webpack.js.org/plugins/ for a list of some available plugins.
 * @param {Array}
 */
const plugins = [
  new webpack.optimize.UglifyJsPlugin({
    // ...
  }),
];

/*
 * These options change how modules are resolved.  webpack provides reasonable
 * defaults, but it is possible to change the resolving in detail.  Needing to
 * change the defaults is not common.  See
 * https://webpack.js.org/configuration/resolve/ for details.
 *
 * @param {!Object=}
 */
const resolve = undefined;

/*
 * Because JavaScript can be written for both server and browser, webpack offers
 * multiple deployment targets that you can set in your webpack configuration.
 *
 * See https://webpack.js.org/configuration/target/ for details.
 *
 * @param {string=} [target='web'] - May be one of `'async-node'`,
 *   `'electron-main'`, `'electron-renderer'`, `'node'`, `'node-webkit'`,
 *   `'web'`, or `'webworker'`.
 * @param {function (compiler): string=} target - Set it to a function if none of
 *   the predefined targets from the list above meet your needs.
 */
const target = undefined;

const config = {
  entry: entry,
  module: loaders,
  externals: externals,
  output: output,
  performance: performance,
  plugins: plugins,
  resolve: resolve,
  target: target,
};

module.exports = config;

// You can also setup more than one configuration.

const config2 = { /* ... */ };

// ...

const configN = { /* ... */ };

module.exports = [
  config,
  config2,
  // ...
  configN,
];
<|endoftext|>