<commit_before>
var SoundCloudAudio = Class.extend({

	clientId: "4d9749247dccda26471f3fa442daa07d",

	init: function () {

	    this.tracks = [];
	    this.trackPos = 0;

	    this.playlistURL = "http://soundcloud.com/jeremygold/favorites";
	    console && console.log(this.playlistURL);
	    var args = window.location.search.substring(1).split("&");
	    for (var i = 0; i < args.length; i++) {
			var arg = args[i].split("=");
			if (arg[0] == "tracks") {
			    this.playlistURL = unescape(arg[1]);
			    break;
			}
	    }

	    var smjs = document.createElement("script");
	    smjs.type = "text/javascript";

	    milk.soundCloudJSONCallback = this.gotStreamURL;
	    smjs.onload = function() {
			soundManager.url = "SoundManager2/";
			soundManager.usePolicyFile = true;
			soundManager.flashVersion = 9;
			soundManager.useHTML5Audio = false;
			soundManager.useFlashBlock = false;
			soundManager.useHighPerformance = true;
			soundManager.wmode = 'transparent';
			soundManager.useFastPolling = true;
			soundManager.useWaveformData = true;
			soundManager.onready(function() {
				var jsonp = document.createElement("script");
				jsonp.type = "text/javascript";
				jsonp.src = "http://api.soundcloud.com/resolve.json?url=" + escape(audio.playlistURL) +
				            "&client_id=" + audio.clientId + "&callback=milk.soundCloudJSONCallback";
				document.body.appendChild(jsonp);
			});
	    };
	    smjs.src = "SoundManager2/soundmanager2.js";
	    document.body.appendChild(smjs);
	},

	gotStreamURL: function(response) {
	    var songs;
	    if ("tracks" in response)
			songs = response.tracks;
	    else if (0 in response)
			songs = response;
	    else
			songs = [response];
    	audio.songs = songs;

	    for (var i = 0; i < songs.length; i++) {
			var song = songs[i];
			var url = song.stream_url + ((song.stream_url.indexOf("?") == -1) ? "?" : "&") + "client_id=" + audio.clientId;
			var trackId = "track_" + song.id;
			audio.tracks.push(trackId);

			soundManager.createSound({
				id: trackId,
				url: url,
				autoPlay: (i == 0),
				useWaveformData: true,
				whileplaying: function() {
				    if (typeof shaker != "undefined") {
						var left = this.waveformData.left;
						var right = this.waveformData.right;
						for (i = 0; i < 256; i++) {
						    left[i] = parseFloat(left[i]);
						    right[i] = parseFloat(right[i]);
						}
						shaker.music.addPCM(left, right);
			    	}
				},
				onplay: function() {
				    var s = audio.songs[audio.trackPos];
				    shaker.infoMessages["SoundCloud"] = "music courtesy of " + s.user.username + "" +
					" - " + s.title + " - " +
					"powered by soundcloud";
				},
				onfinish: function() {
				    soundManager.stopAll();
				    if (audio.trackPos < audio.tracks.length - 1)
					audio.trackPos++;
				    soundManager.play(audio.tracks[audio.trackPos]);
				}
		 	});
	    }

    	SCLoadedCallback();
	},

	updateInfoBox: function(info) {
	    this.infoBox.innerHTML = info;
	}

    });<commit_msg>Fixed UI unresponsiveness caused by error from non-streamable song<commit_after>
var SoundCloudAudio = Class.extend({

	clientId: "4d9749247dccda26471f3fa442daa07d",

	init: function () {

	    this.tracks = [];
	    this.trackPos = 0;

	    this.playlistURL = "http://soundcloud.com/jeremygold/favorites";
	    console && console.log(this.playlistURL);
	    var args = window.location.search.substring(1).split("&");
	    for (var i = 0; i < args.length; i++) {
			var arg = args[i].split("=");
			if (arg[0] == "tracks") {
			    this.playlistURL = unescape(arg[1]);
			    break;
			}
	    }

	    var smjs = document.createElement("script");
	    smjs.type = "text/javascript";

	    milk.soundCloudJSONCallback = this.gotStreamURL;
	    smjs.onload = function() {
			soundManager.url = "SoundManager2/";
			soundManager.usePolicyFile = true;
			soundManager.flashVersion = 9;
			soundManager.useHTML5Audio = false;
			soundManager.useFlashBlock = false;
			soundManager.useHighPerformance = true;
			soundManager.wmode = 'transparent';
			soundManager.useFastPolling = true;
			soundManager.useWaveformData = true;
			soundManager.onready(function() {
				var jsonp = document.createElement("script");
				jsonp.type = "text/javascript";
				jsonp.src = "http://api.soundcloud.com/resolve.json?url=" + escape(audio.playlistURL) +
				            "&client_id=" + audio.clientId + "&callback=milk.soundCloudJSONCallback";
				document.body.appendChild(jsonp);
			});
	    };
	    smjs.src = "SoundManager2/soundmanager2.js";
	    document.body.appendChild(smjs);
	},

	gotStreamURL: function(response) {
	    var songs;
	    if ("tracks" in response)
			songs = response.tracks;
	    else if (0 in response)
			songs = response;
	    else
			songs = [response];
    	audio.songs = songs;

	    for (var i = 0; i < songs.length; i++) {
			var song = songs[i];
			if (! song.streamable) {
				continue;
			}
			var url = song.stream_url + ((song.stream_url.indexOf("?") == -1) ? "?" : "&") + "client_id=" + audio.clientId;
			var trackId = "track_" + song.id;
			audio.tracks.push(trackId);

			soundManager.createSound({
				id: trackId,
				url: url,
				autoPlay: (i == 0),
				useWaveformData: true,
				whileplaying: function() {
				    if (typeof shaker != "undefined") {
						var left = this.waveformData.left;
						var right = this.waveformData.right;
						for (i = 0; i < 256; i++) {
						    left[i] = parseFloat(left[i]);
						    right[i] = parseFloat(right[i]);
						}
						shaker.music.addPCM(left, right);
			    	}
				},
				onplay: function() {
				    var s = audio.songs[audio.trackPos];
				    shaker.infoMessages["SoundCloud"] = "music courtesy of " + s.user.username + "" +
					" - " + s.title + " - " +
					"powered by soundcloud";
				},
				onfinish: function() {
				    soundManager.stopAll();
				    if (audio.trackPos < audio.tracks.length - 1)
					audio.trackPos++;
				    soundManager.play(audio.tracks[audio.trackPos]);
				}
		 	});
	    }

    	SCLoadedCallback();
	},

	updateInfoBox: function(info) {
	    this.infoBox.innerHTML = info;
	}

    });<|endoftext|>


<commit_before>var ensure = require('./ensure');
var type = require('./type');
var arrayify = require('./arrayify');

// if (fields.menu) {

//   updates.menu = [];

//   for (var i in fields) {

//     if (i.slice(0, 6) === 'title_') {

//       var id = i.slice(6),
//           link = {
//             id: id,
//             url: fields['url_' + id],
//             label: fields[i]
//           };

//       if (link.url && link.label)
//         updates.menu.push(link);
//     }
//   }
// }

// how to implement some rudimentary type coercion?
// would be nice to coerce strings -> numbs
// and strings -> bools

function formJSON (fields, model) {

  // console.log(fields);
  // console.log(model);

  if (model === undefined) model = {};

  ensure(fields, 'object')
    .and(model, 'object');

  var obj = {};

  for (var i in fields) {

    // Sometimes there are multiple values
    // for one field. Collapse them to one.
    if (type(fields[i]) === 'array') {
      fields[i] = fields[i][0];
      console.log('FormJSON: Multiple inputs with same name', i, fields[i]);
    }

    var terms = i.split('.'),
        totalTerms = terms.length,
        val = fields[i];

    var parent = obj,
        modelDef = model;

    for (var j = 0; j < terms.length;j++) {

      var key = terms[j];

      // Recurse down the model tree too
      // until we reach a definition
      if (modelDef && modelDef[key]) {
        modelDef = modelDef[key];
      } else {
        modelDef = false;
      }

      parent[key] = parent[key] || {};

      // At the leaf node
      // final term in list of terms
      if ((j + 1) === totalTerms) {

        // At lowest level of type definition
        // and the types do not match
        if (type(modelDef) === 'string' && type(val) !== modelDef) {

          try {

            if (modelDef === 'string') val += '';

            if (modelDef === 'number') val = parseFloat(val);

            if (modelDef === 'boolean') {

              if (val === 'off') {
                val = false;
              } else if (val === 'on') {
                val = true;
              } else {
                val = parseBool(val);
              }
            }

            if (modelDef === 'object' ||
                modelDef === 'array') val = JSON.parse(val);

          } catch (e) {
            console.log('Could not coerce val to desired ' + modelDef + ' it is currently: ');
            console.log(val);
            console.log()
            console.log(e)
          }

        }

        parent[key] = val;
      }

      parent = parent[key];
    }
  }

  function catchArrays(obj, model) {

    for (var i in obj) {

      // console.log(i);

      if (type(model[i])  === 'array') {

        // console.log(i);
        // console.log(obj[i]);
        // console.log(arrayify(obj[i]));
        obj[i] = arrayify(obj[i]);
      }

      if (type(model[i]) === 'object') {

        // console.log(obj[i]);
        // console.log(model[i]);

        catchArrays(obj[i], model[i]);
      }
    }
  }

  catchArrays(obj, model);

  ensure(obj, model);

  return obj;
}

function parseBool (string){
  switch(string.trim().toLowerCase()){
    case "true": case "yes": case "1": return true;
    case "false": case "no": case "0": case null: return false;
    default: return Boolean(string);
  }
}

function unitTests () {

  var assert = require('assert');

  var field1 = {
    'foo.baz': 1,
    'foo.bar': 2,
    'foo.bat.cat': 3,
    'foo.bat.bar': 4
  };

  var model1 = {
    foo: {
      baz: 'number',
      bar: 'number',
      bat: {
        cat: 'number',
        bar: 'string'
      }
    }
  };

  var result1 = {
    foo: {
      baz: 1,
      bar: 2,
      bat: {
        cat: 3,
        bar: '4'
      }
    }
  };

  // assert.deepEqual(formJSON(field1, model1), result1);

  var field2 = {
    'foo.bar.1.title': 'Hello',
    'foo.bar.1.url': 'Bye',
    'foo.bar.1.id': '1',
    'foo.bar.2.title': 'OK',
    'foo.bar.2.url': 'GO',
    'foo.bar.2.id': '2',
  };

  var model2 = {
    foo: {
      bar: [
        {id: 'string', title: 'string', url: 'string'}
      ]
    }
  };

  var expected2 = {
    foo: {
      bar: [
        {title: 'Hello', url: 'Bye', id: '1'},
        {title: 'OK', url: 'GO', id: '2'},
      ]
    }
  };

  var resulted = {
    "foo":{
      "bar":[
        {
          "title":"Hello",
          "url":"Bye",
          "id":"1"
        },
        {
          "title":"OK",
          "url":"GO",
          "id":"2"
        }
      ]
    }
  };

    var result2 = formJSON(field2, model2);

  // console.log(JSON.stringify(result2));

  // assert.deepEqual(resulted, expected2);

  assert.deepEqual(result2, expected2);

};

module.exports = formJSON;<commit_msg>improve logging from form json<commit_after>var ensure = require('./ensure');
var type = require('./type');
var arrayify = require('./arrayify');

// if (fields.menu) {

//   updates.menu = [];

//   for (var i in fields) {

//     if (i.slice(0, 6) === 'title_') {

//       var id = i.slice(6),
//           link = {
//             id: id,
//             url: fields['url_' + id],
//             label: fields[i]
//           };

//       if (link.url && link.label)
//         updates.menu.push(link);
//     }
//   }
// }

// how to implement some rudimentary type coercion?
// would be nice to coerce strings -> numbs
// and strings -> bools

function formJSON (fields, model) {

  // console.log(fields);
  // console.log(model);

  if (model === undefined) model = {};

  ensure(fields, 'object')
    .and(model, 'object');

  var obj = {};

  for (var i in fields) {

    // Sometimes there are multiple values
    // for one field. Collapse them to one.
    if (type(fields[i]) === 'array') {
      fields[i] = fields[i].pop();
      console.log('FormJSON: Multiple inputs with same name', i, fields[i]);
    }

    var terms = i.split('.'),
        totalTerms = terms.length,
        val = fields[i];

    var parent = obj,
        modelDef = model;

    for (var j = 0; j < terms.length;j++) {

      var key = terms[j];

      // Recurse down the model tree too
      // until we reach a definition
      if (modelDef && modelDef[key]) {
        modelDef = modelDef[key];
      } else {
        modelDef = false;
      }

      parent[key] = parent[key] || {};

      // At the leaf node
      // final term in list of terms
      if ((j + 1) === totalTerms) {

        // At lowest level of type definition
        // and the types do not match
        if (type(modelDef) === 'string' && type(val) !== modelDef) {

          try {

            if (modelDef === 'string') val += '';

            if (modelDef === 'number') val = parseFloat(val);

            if (modelDef === 'boolean') {

              if (val === 'off') {
                val = false;
              } else if (val === 'on') {
                val = true;
              } else {
                val = parseBool(val);
              }
            }

            if (modelDef === 'object' ||
                modelDef === 'array') val = JSON.parse(val);

          } catch (e) {
            console.log('Could not coerce val to desired ' + modelDef + ' it is currently: ');
            console.log(val);
            console.log()
            console.log(e)
          }

        }

        parent[key] = val;
      }

      parent = parent[key];
    }
  }

  function catchArrays(obj, model) {

    for (var i in obj) {

      // console.log(i);

      if (type(model[i])  === 'array') {

        obj[i] = arrayify(obj[i], function(item){

          // arrayidy adds cruft like the name property
          // make sure we remove anything thats not in the
          // model definition for this item
          for (var x in item)
            if (model[i][0][x] === undefined) {
              delete item[x];
            }

          // this fixes a bug with nested objects in arrays
          // and is neccessary for blog.menu
          if (!Object.keys(item).length) return false;

          return item;
        });
      }

      if (type(model[i]) === 'object') {

        // console.log(obj[i]);
        // console.log(model[i]);

        catchArrays(obj[i], model[i]);
      }
    }
  }

  catchArrays(obj, model);

  ensure(obj, model);

  return obj;
}

function parseBool (string){
  switch(string.trim().toLowerCase()){
    case "true": case "yes": case "1": return true;
    case "false": case "no": case "0": case null: return false;
    default: return Boolean(string);
  }
}

function unitTests () {

  var assert = require('assert');

  var field1 = {
    'foo.baz': 1,
    'foo.bar': 2,
    'foo.bat.cat': 3,
    'foo.bat.bar': 4
  };

  var model1 = {
    foo: {
      baz: 'number',
      bar: 'number',
      bat: {
        cat: 'number',
        bar: 'string'
      }
    }
  };

  var result1 = {
    foo: {
      baz: 1,
      bar: 2,
      bat: {
        cat: 3,
        bar: '4'
      }
    }
  };

  // assert.deepEqual(formJSON(field1, model1), result1);

  var field2 = {
    'foo.bar.1.title': 'Hello',
    'foo.bar.1.url': 'Bye',
    'foo.bar.1.id': '1',
    'foo.bar.2.title': 'OK',
    'foo.bar.2.url': 'GO',
    'foo.bar.2.id': '2',
  };

  var model2 = {
    foo: {
      bar: [
        {id: 'string', title: 'string', url: 'string'}
      ]
    }
  };

  var expected2 = {
    foo: {
      bar: [
        {title: 'Hello', url: 'Bye', id: '1'},
        {title: 'OK', url: 'GO', id: '2'},
      ]
    }
  };

  var resulted = {
    "foo":{
      "bar":[
        {
          "title":"Hello",
          "url":"Bye",
          "id":"1"
        },
        {
          "title":"OK",
          "url":"GO",
          "id":"2"
        }
      ]
    }
  };

    var result2 = formJSON(field2, model2);

  // console.log(JSON.stringify(result2));

  // assert.deepEqual(resulted, expected2);

  assert.deepEqual(result2, expected2);

};

module.exports = formJSON;<|endoftext|>


<commit_before>// LIB_ObservableSet inherits from LIB_Set.
//
var LIB_ObservableSet = function() {
    LIB_Set.apply(this, arguments);
};

LIB_ObservableSet.prototype = new LIB_Set();
LIB_ObservableSet.prototype.constructor = LIB_ObservableSet;

LIB_mixinSubject(LIB_ObservableSet.prototype);

// Wrap the set mutator methods to dispatch events.

LIB_ObservableSet.prototype.add = function() {
    var added = [];
    for (var i=0, ilen=arguments.length; i<ilen; i++) {
        var argument = arguments[i];
        if (LIB_Set.prototype.add.call(this, argument)) {
            added.push(argument);
            if (LIB_implementsSubject(argument)) {
                argument.addEventListener('LIB_all', this.elementListener, this);
            }
        }
    }
    var modified = added.length > 0;
    if (modified) {
        this.dispatchEvent({type: 'LIB_add', relatedTargets: added, cancelBubble: true});
        this.dispatchEvent({type: 'LIB_afterAdd', relatedTargets: added});
    }
    return modified;
};

LIB_ObservableSet.prototype['delete'] = function() {
    var deleted = [];
    for (var i=0, ilen=arguments.length; i<ilen; i++) {
        var argument = arguments[i];
        if (LIB_Set.prototype['delete'].call(this, argument)) {
            deleted.push(argument);
            if (LIB_implementsSubject(argument)) {
                argument.removeEventListener('LIB_all', this.elementListener, this);
            }
        }
    }
    var modified = deleted.length > 0;
    if (modified) {
        this.dispatchEvent({type: 'LIB_delete', relatedTargets: deleted, cancelBubble: true});
        this.dispatchEvent({type: 'LIB_afterDelete', relatedTargets: deleted});
    }
    return modified;
};

LIB_ObservableSet.prototype.empty = function() {
    var deleted = this.toArray();
    var result = LIB_Set.prototype.empty.call(this);
    if (result) {
        this.dispatchEvent({type: 'LIB_delete', relatedTargets: deleted, cancelBubble: true});
        this.dispatchEvent({type: 'LIB_afterDelete', relatedTargets: deleted});
    }
    return result;
};

LIB_ObservableSet.prototype.elementListener = function(ev) {
    // bubble the event
    if (!ev.cancelBubble) {
        this.dispatchEvent(ev);
    }

    // If it is a destroy event being dispatched on the
    // destroyed element then we want to remove it from
    // this set.
    if ((ev.type === 'LIB_destroy') &&
        (ev.currentTarget === ev.target)) {
        this['delete'](ev.target);
    }
};
<commit_msg>bubble after handling the event. Hard to say which order is better but it seems better to me at the moment to bubble after<commit_after>// LIB_ObservableSet inherits from LIB_Set.
//
var LIB_ObservableSet = function() {
    LIB_Set.apply(this, arguments);
};

LIB_ObservableSet.prototype = new LIB_Set();
LIB_ObservableSet.prototype.constructor = LIB_ObservableSet;

LIB_mixinSubject(LIB_ObservableSet.prototype);

// Wrap the set mutator methods to dispatch events.

LIB_ObservableSet.prototype.add = function() {
    var added = [];
    for (var i=0, ilen=arguments.length; i<ilen; i++) {
        var argument = arguments[i];
        if (LIB_Set.prototype.add.call(this, argument)) {
            added.push(argument);
            if (LIB_implementsSubject(argument)) {
                argument.addEventListener('LIB_all', this.elementListener, this);
            }
        }
    }
    var modified = added.length > 0;
    if (modified) {
        this.dispatchEvent({type: 'LIB_add', relatedTargets: added, cancelBubble: true});
        this.dispatchEvent({type: 'LIB_afterAdd', relatedTargets: added});
    }
    return modified;
};

LIB_ObservableSet.prototype['delete'] = function() {
    var deleted = [];
    for (var i=0, ilen=arguments.length; i<ilen; i++) {
        var argument = arguments[i];
        if (LIB_Set.prototype['delete'].call(this, argument)) {
            deleted.push(argument);
            if (LIB_implementsSubject(argument)) {
                argument.removeEventListener('LIB_all', this.elementListener, this);
            }
        }
    }
    var modified = deleted.length > 0;
    if (modified) {
        this.dispatchEvent({type: 'LIB_delete', relatedTargets: deleted, cancelBubble: true});
        this.dispatchEvent({type: 'LIB_afterDelete', relatedTargets: deleted});
    }
    return modified;
};

LIB_ObservableSet.prototype.empty = function() {
    var deleted = this.toArray();
    var result = LIB_Set.prototype.empty.call(this);
    if (result) {
        this.dispatchEvent({type: 'LIB_delete', relatedTargets: deleted, cancelBubble: true});
        this.dispatchEvent({type: 'LIB_afterDelete', relatedTargets: deleted});
    }
    return result;
};

LIB_ObservableSet.prototype.elementListener = function(ev) {

    // If it is a destroy event being dispatched on the
    // destroyed element then we want to remove it from
    // this set.
    if ((ev.type === 'LIB_destroy') &&
        (ev.currentTarget === ev.target)) {
        this['delete'](ev.target);
    }

    // bubble the event
    if (!ev.cancelBubble) {
        this.dispatchEvent(ev);
    }

};
<|endoftext|>


<commit_before>define([
	"dojo/_base/declare",	// declare
	"dojo/has",				// has("dijit-legacy-requires")
	"dojo/_base/kernel",	// kernel.deprecated
	"dojo/ready",
	"../_Widget",
	"../_CssStateMixin",
	"../_TemplatedMixin",
	"./_FormWidgetMixin"
], function(declare, has, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){

// module:
//		dijit/form/_FormWidget

// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
	ready(0, function(){
		var requires = ["dijit/form/_FormValueWidget"];
		require(requires);	// use indirection so modules not rolled into a build
	});
}

return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
	// summary:
	//		Base class for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
	//		which can be children of a `<form>` node or a `dijit/form/Form` widget.
	//
	// description:
	//		Represents a single HTML element.
	//		All these widgets should have these attributes just like native HTML input elements.
	//		You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
	//
	//		They also share some common methods.

	setDisabled: function(/*Boolean*/ disabled){
		// summary:
		//		Deprecated.  Use set('disabled', ...) instead.
		kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
		this.set('disabled', disabled);
	},

	setValue: function(/*String*/ value){
		// summary:
		//		Deprecated.  Use set('value', ...) instead.
		kernel.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated.  Use set('value',"+value+") instead.", "", "2.0");
		this.set('value', value);
	},

	getValue: function(){
		// summary:
		//		Deprecated.  Use get('value') instead.
		kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
		return this.get('value');
	},

	postMixInProperties: function(){
		// Setup name=foo string to be referenced from the template (but only if a name has been specified)
		// Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8484, #8660.
		// Regarding escaping, see heading "Attribute values" in
		// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
		this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/"/g, """) + '"') : '';
		this.inherited(arguments);
	},

	// Override automatic assigning type --> focusNode, it causes exception on IE.
	// Instead, type must be specified as ${type} in the template, as part of the original DOM
	_setTypeAttr: null
});

});
<commit_msg>Windows 8 Store Apps throw an exception when trying to set an element's name attribute via innerHTML.   So avoid doing that on Windows 8 Apps.   Instead, do it via the _setNameAttr custom setter.   This is how it should be done for all platforms starting in 2.0.<commit_after>define([
	"dojo/_base/declare",	// declare
	"dojo/sniff",			// has("dijit-legacy-requires"), has("win8app")
	"dojo/_base/kernel",	// kernel.deprecated
	"dojo/ready",
	"../_Widget",
	"../_CssStateMixin",
	"../_TemplatedMixin",
	"./_FormWidgetMixin"
], function(declare, has, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){

// module:
//		dijit/form/_FormWidget

// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
	ready(0, function(){
		var requires = ["dijit/form/_FormValueWidget"];
		require(requires);	// use indirection so modules not rolled into a build
	});
}

return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
	// summary:
	//		Base class for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
	//		which can be children of a `<form>` node or a `dijit/form/Form` widget.
	//
	// description:
	//		Represents a single HTML element.
	//		All these widgets should have these attributes just like native HTML input elements.
	//		You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
	//
	//		They also share some common methods.

	setDisabled: function(/*Boolean*/ disabled){
		// summary:
		//		Deprecated.  Use set('disabled', ...) instead.
		kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
		this.set('disabled', disabled);
	},

	setValue: function(/*String*/ value){
		// summary:
		//		Deprecated.  Use set('value', ...) instead.
		kernel.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated.  Use set('value',"+value+") instead.", "", "2.0");
		this.set('value', value);
	},

	getValue: function(){
		// summary:
		//		Deprecated.  Use get('value') instead.
		kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
		return this.get('value');
	},

	postMixInProperties: function(){
		// Setup name=foo string to be referenced from the template (but only if a name has been specified).
		// Unfortunately we can't use _setNameAttr to set the name in IE due to IE limitations, see #8484, #8660.
		// But when IE6 and IE7 are desupported, then we probably don't need this anymore, so should remove it in 2.0.
		// Also, don't do this for Windows 8 Store Apps because it causes a security exception (see #16452).
		// Regarding escaping, see heading "Attribute values" in
		// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
		this.nameAttrSetting = (this.name && !has("win8app")) ? ('name="' + this.name.replace(/"/g, """) + '"') : '';
		this.inherited(arguments);
	},

	// Override automatic assigning type --> focusNode, it causes exception on IE.
	// Instead, type must be specified as ${type} in the template, as part of the original DOM
	_setTypeAttr: null
});

});
<|endoftext|>


<commit_before><commit_msg>remove debug<commit_after><|endoftext|>


<commit_before>var
_doc = document,
_win = window,
init = function(){
    smoothScrolling();
	fancy_functions();
    fitContentOnScreen();
},
resize = function(){
    fitContentOnScreen();
};

_doc.addEventListener('DOMContentLoaded', init);
_win.addEventListener('resize', resize);

// Service worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('./service-worker.js');
}
<commit_msg>don't need to call smoothScrolling<commit_after>var
_doc = document,
_win = window,
init = function(){
    fancy_functions();
    fitContentOnScreen();
},
resize = function(){
    fitContentOnScreen();
};

_doc.addEventListener('DOMContentLoaded', init);
_win.addEventListener('resize', resize);

// Service worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('./service-worker.js');
}
<|endoftext|>


<commit_before>const i18n = require('i18next')
const fsBackend = require('i18next-fs-backend')
const middleware = require('i18next-http-middleware')
const path = require('path')

module.exports = {
  setup(options = {}) {
    const subdomainLang = options.subdomainLang || {}
    const availableLngs = Object.values(subdomainLang).map(c => c.lngCode)

    i18n.use(fsBackend).init({
      backend: {
        loadPath: path.join(__dirname, '../../locales/__lng__.json')
      },

      // Load translation files synchronously: https://www.i18next.com/overview/configuration-options#initimmediate
      initImmediate: false,

      // We use the legacy v1 JSON format, so configure interpolator to use
      // underscores instead of curly braces
      interpolation: {
        prefix: '__',
        suffix: '__'
      },

      preload: availableLngs,
      supportedLngs: availableLngs,
      fallbackLng: options.defaultLng || 'en'
    })

    const langDetector = new middleware.LanguageDetector(i18n.services)

    function setLangBasedOnDomainMiddleware(req, res, next) {
      // Determine language from subdomain
      const { host } = req.headers
      if (host == null) {
        return next()
      }
      const [subdomain] = host.split(/[.-]/)
      const lang = subdomainLang[subdomain]
        ? subdomainLang[subdomain].lngCode
        : null

      // Unless setLng query param is set, use subdomain lang
      if (!req.originalUrl.includes('setLng') && lang != null) {
        req.i18n.changeLanguage(lang)
      }

      // If the set language is different from the language detection (based on
      // the Accept-Language header), then set flag which will show a banner
      // offering to switch to the appropriate library
      const detectedLanguage = langDetector.detect(req, res)
      if (req.language !== detectedLanguage) {
        req.showUserOtherLng = detectedLanguage
      }

      next()
    }

    const expressMiddleware = function(req, res, next) {
      middleware.handle(i18n)(req, res, (...args) => {
        // Decorate req.i18n with translate function alias for backwards
        // compatibility
        req.i18n.translate = req.i18n.t
        next(...args)
      })
    }

    return {
      expressMiddleware,
      setLangBasedOnDomainMiddleware,
      i18n,

      // Backwards compatibility with long-standing typo
      expressMiddlewear: expressMiddleware,
      setLangBasedOnDomainMiddlewear: setLangBasedOnDomainMiddleware
    }
  }
}
<commit_msg>Fix interpolation problem with further config<commit_after>const i18n = require('i18next')
const fsBackend = require('i18next-fs-backend')
const middleware = require('i18next-http-middleware')
const path = require('path')

module.exports = {
  setup(options = {}) {
    const subdomainLang = options.subdomainLang || {}
    const availableLngs = Object.values(subdomainLang).map(c => c.lngCode)

    i18n.use(fsBackend).init({
      backend: {
        loadPath: path.join(__dirname, '../../locales/__lng__.json')
      },

      // Load translation files synchronously: https://www.i18next.com/overview/configuration-options#initimmediate
      initImmediate: false,

      // We use the legacy v1 JSON format, so configure interpolator to use
      // underscores instead of curly braces
      interpolation: {
        prefix: '__',
        suffix: '__',
        unescapeSuffix: 'HTML'
      },

      preload: availableLngs,
      supportedLngs: availableLngs,
      fallbackLng: options.defaultLng || 'en'
    })

    const langDetector = new middleware.LanguageDetector(i18n.services)

    function setLangBasedOnDomainMiddleware(req, res, next) {
      // Determine language from subdomain
      const { host } = req.headers
      if (host == null) {
        return next()
      }
      const [subdomain] = host.split(/[.-]/)
      const lang = subdomainLang[subdomain]
        ? subdomainLang[subdomain].lngCode
        : null

      // Unless setLng query param is set, use subdomain lang
      if (!req.originalUrl.includes('setLng') && lang != null) {
        req.i18n.changeLanguage(lang)
      }

      // If the set language is different from the language detection (based on
      // the Accept-Language header), then set flag which will show a banner
      // offering to switch to the appropriate library
      const detectedLanguage = langDetector.detect(req, res)
      if (req.language !== detectedLanguage) {
        req.showUserOtherLng = detectedLanguage
      }

      next()
    }

    const expressMiddleware = function(req, res, next) {
      middleware.handle(i18n)(req, res, (...args) => {
        // Decorate req.i18n with translate function alias for backwards
        // compatibility
        req.i18n.translate = req.i18n.t
        next(...args)
      })
    }

    return {
      expressMiddleware,
      setLangBasedOnDomainMiddleware,
      i18n,

      // Backwards compatibility with long-standing typo
      expressMiddlewear: expressMiddleware,
      setLangBasedOnDomainMiddlewear: setLangBasedOnDomainMiddleware
    }
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

/** @license
 * This file is part of the Game Closure SDK.
 *
 * The Game Closure SDK is free software: you can redistribute it and/or modify
 * it under the terms of the Mozilla Public License v. 2.0 as published by
 * Mozilla.
 *
 * The Game Closure SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Mozilla Public License v. 2.0 for more details.
 *
 * You should have received a copy of the Mozilla Public License v. 2.0
 * along with the Game Closure SDK.  If not, see <http://mozilla.org/MPL/2.0/>.
 */

process.title = 'devkit';

/**
 * Command line interface.
 */

/* jshint -W020 */
require = require('jsio');
/* jshint +W020 */

require('./globals');

var logging = require('./util/logging');
var logger = logging.get('devkit');

/**
 * Module API.
 */

// Command line invocation.
if (require.main === module) {
	main();
}

function main () {
	var commands = require('./commands');
	var argv = commands.argv;
	var args = argv._;

	var name;
	if (commands.has(args[0])) {
		name = args.shift();
	} else if (argv.version) {
		name = 'version';
	} else {
		name = 'help';
	}

	var command = commands.get(name);
	command.exec(args);
}
<commit_msg>fix (devkit cli): add support for nodejs vs node<commit_after>#!/usr/bin/env sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"

/** @license
 * This file is part of the Game Closure SDK.
 *
 * The Game Closure SDK is free software: you can redistribute it and/or modify
 * it under the terms of the Mozilla Public License v. 2.0 as published by
 * Mozilla.
 *
 * The Game Closure SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Mozilla Public License v. 2.0 for more details.
 *
 * You should have received a copy of the Mozilla Public License v. 2.0
 * along with the Game Closure SDK.  If not, see <http://mozilla.org/MPL/2.0/>.
 */

process.title = 'devkit';

/**
 * Command line interface.
 */

/* jshint -W020 */
require = require('jsio');
/* jshint +W020 */

require('./globals');

var logging = require('./util/logging');
var logger = logging.get('devkit');

/**
 * Module API.
 */

// Command line invocation.
if (require.main === module) {
	main();
}

function main () {
	var commands = require('./commands');
	var argv = commands.argv;
	var args = argv._;

	var name;
	if (commands.has(args[0])) {
		name = args.shift();
	} else if (argv.version) {
		name = 'version';
	} else {
		name = 'help';
	}

	var command = commands.get(name);
	command.exec(args);
}
<|endoftext|>


<commit_before>/**
 * Easy Drag and Drop Zone
 *
 * @author Michael F. Spahn <kontakt@michaelspahn.de>
 * @license https://www.gnu.org/licenses/agpl-3.0.html GNU Affero Public License Version 3
 */
window.easydd = function () {
    /**
     * Div Element to render drop zone to
     *
     * @private
     * @type {null}
     */
    easyDD.prototype.context = null;

    /**
     * Text span inside the drop zone
     *
     * @private
     * @type {null}
     */
    easyDD.prototype.span = null;

    /**
     * Hidden input field
     *
     * @private
     * @type {null}
     */
    easyDD.prototype.hiddenInput = null;

    /**
     * Config
     *
     * @private
     * @type {{}}
     */
    easyDD.prototype.config = {};

    /**
     * Contains all uploaded files
     *
     * @private
     * @type {Array}
     */
    easyDD.prototype.fileData = [];

    /**
     * Constructor
     *
     * @param c
     */
    function easyDD(c) {
        this.context = document.getElementById(c.elementId);
        this.config = c;

        if (this.config.text) {
            this.setText(this.config.text);
        }

        if (this.context !== undefined) {
            this.context.addEventListener('dragover', function (evt) {
                this.onDragover(evt, this);
            }.bind(this), false);

            this.context.addEventListener('drop', function (evt) {
                if (typeof this.config.callback === 'function') {
                    var cb = this.config.callback;
                } else {
                    var cb = function (data) {};
                }

                this.onDrop(evt, this, cb);
            }.bind(this), false);

            this.context.addEventListener('click', function (evt) {
                this.onClick(evt);
            }.bind(this), false);

            this.context.classList.add('easyDD');
        }
    }

    /**
     * Event listener if drag over
     *
     * @private
     * @param evt
     */
    easyDD.prototype.onDragover = function (evt) {
        evt.stopPropagation();
        evt.preventDefault();
        evt.dataTransfer.dropEffect = 'copy';
    };

    /**
     * Event listener on drop
     *
     * @private
     * @param evt
     */
    easyDD.prototype.onDrop = function (evt, that, cb) {
        evt.stopPropagation();
        evt.preventDefault();

        if (evt.dataTransfer) {
            var files = evt.dataTransfer.files;

        } else {
            var files = evt.target.files;
        }

        for (var i = 0; i < files.length; i++) {
            this.saveFileDataToArray(files[i], that, cb);
        }
    };

    /**
     * Returns a array of every submitted file
     *
     * @public
     * @returns {Array}
     */
    easyDD.prototype.getData = function () {
        return this.fileData;
    };

    /**
     * @private
     */
    easyDD.prototype.saveFileDataToArray = function (data, that, cb) {
        var reader = new FileReader();

        try {
            reader.onload = (function (file) {
                return function (e) {
                    // Check for mimetype if enabled
                    if (that.config.allowedMimeTypes.length > 0) {
                        if (that.config.allowedMimeTypes.indexOf(file.type) === -1) {
                            cb(-1);
                            return;
                        }
                    }

                    // Check for filesize if enabled
                    if (that.config.maxSize > 0) {
                        if ((file.size / 1024) > that.config.maxSize) {
                            cb(-2);
                            return;
                        }
                    }

                    cb(e.target.result);
                    that.fileData.push(e.target.result);
                };
            })(data);

            reader.readAsDataURL(data);
        } catch (e) {
            console.warn('Something went wrong.');
        }
    };

    /**
     * Render hidden input field to fake file upload on click
     *
     * @private
     */
    easyDD.prototype.addHiddenInput = function () {
        if (this.hiddenInput === null) {
            this.hiddenInput = document.createElement('input');
            this.hiddenInput.setAttribute('name', 'easyddHI');
            this.hiddenInput.setAttribute('type', 'file');
            this.hiddenInput.setAttribute('id', 'easyddHi');
            this.context.appendChild(this.hiddenInput);

            this.hiddenInput.addEventListener('change', function (evt) {
                if (typeof this.config.callback === 'function') {
                    var cb = this.config.callback;
                } else {
                    var cb = function (data) {};
                }

                this.onDrop(evt, this, cb);
            }.bind(this), false);
        }
    };

    /**
     * Event listener on click
     *
     * @private
     * @param evt
     */
    easyDD.prototype.onClick = function (evt) {
        this.addHiddenInput();
        this.hiddenInput.click();
    };

    /**
     * Set text on dropzone
     *
     * @public
     * @param text
     */
    easyDD.prototype.setText = function (text) {
        if (this.span == null) {
            this.span = document.createElement('span');
            this.span.classList.add('easyDDspan');
            this.context.appendChild(this.span);
        }

        this.span.innerHTML = text;
    };

    var easydd = {
        create: function (c) {
            return new easyDD(c);
        }
    };
    return easydd;
}();<commit_msg>Fix: Multiple upload does not work by click<commit_after>/**
 * Easy Drag and Drop Zone
 *
 * @author Michael F. Spahn <kontakt@michaelspahn.de>
 * @license https://www.gnu.org/licenses/agpl-3.0.html GNU Affero Public License Version 3
 */
window.easydd = function () {
    /**
     * Div Element to render drop zone to
     *
     * @private
     * @type {null}
     */
    easyDD.prototype.context = null;

    /**
     * Text span inside the drop zone
     *
     * @private
     * @type {null}
     */
    easyDD.prototype.span = null;

    /**
     * Hidden input field
     *
     * @private
     * @type {null}
     */
    easyDD.prototype.hiddenInput = null;

    /**
     * Config
     *
     * @private
     * @type {{}}
     */
    easyDD.prototype.config = {};

    /**
     * Contains all uploaded files
     *
     * @private
     * @type {Array}
     */
    easyDD.prototype.fileData = [];

    /**
     * Constructor
     *
     * @param c
     */
    function easyDD(c) {
        this.context = document.getElementById(c.elementId);
        this.config = c;

        if (this.config.text) {
            this.setText(this.config.text);
        }

        if (this.context !== undefined) {
            this.context.addEventListener('dragover', function (evt) {
                this.onDragover(evt, this);
            }.bind(this), false);

            this.context.addEventListener('drop', function (evt) {
                if (typeof this.config.callback === 'function') {
                    var cb = this.config.callback;
                } else {
                    var cb = function (data) {};
                }

                this.onDrop(evt, this, cb);
            }.bind(this), false);

            this.context.addEventListener('click', function (evt) {
                this.onClick(evt);
            }.bind(this), false);

            this.context.classList.add('easyDD');
        }
    }

    /**
     * Event listener if drag over
     *
     * @private
     * @param evt
     */
    easyDD.prototype.onDragover = function (evt) {
        evt.stopPropagation();
        evt.preventDefault();
        evt.dataTransfer.dropEffect = 'copy';
    };

    /**
     * Event listener on drop
     *
     * @private
     * @param evt
     */
    easyDD.prototype.onDrop = function (evt, that, cb) {
        evt.stopPropagation();
        evt.preventDefault();

        if (evt.dataTransfer) {
            var files = evt.dataTransfer.files;

        } else {
            var files = evt.target.files;
        }

        for (var i = 0; i < files.length; i++) {
            this.saveFileDataToArray(files[i], that, cb);
        }
    };

    /**
     * Returns a array of every submitted file
     *
     * @public
     * @returns {Array}
     */
    easyDD.prototype.getData = function () {
        return this.fileData;
    };

    /**
     * @private
     */
    easyDD.prototype.saveFileDataToArray = function (data, that, cb) {
        var reader = new FileReader();

        try {
            reader.onload = (function (file) {
                return function (e) {
                    // Check for mimetype if enabled
                    if (that.config.allowedMimeTypes.length > 0) {
                        if (that.config.allowedMimeTypes.indexOf(file.type) === -1) {
                            cb(-1);
                            return;
                        }
                    }

                    // Check for filesize if enabled
                    if (that.config.maxSize > 0) {
                        if ((file.size / 1024) > that.config.maxSize) {
                            cb(-2);
                            return;
                        }
                    }

                    cb(e.target.result);
                    that.fileData.push(e.target.result);
                };
            })(data);

            reader.readAsDataURL(data);
        } catch (e) {
            console.warn('Something went wrong.');
        }
    };

    /**
     * Render hidden input field to fake file upload on click
     *
     * @private
     */
    easyDD.prototype.addHiddenInput = function () {
        if (this.hiddenInput === null) {
            this.hiddenInput = document.createElement('input');
            this.hiddenInput.setAttribute('name', 'easyddHI');
            this.hiddenInput.setAttribute('type', 'file');
            this.hiddenInput.setAttribute('id', 'easyddHi');
            this.hiddenInput.multiple = true;
            this.context.appendChild(this.hiddenInput);

            this.hiddenInput.addEventListener('change', function (evt) {
                if (typeof this.config.callback === 'function') {
                    var cb = this.config.callback;
                } else {
                    var cb = function (data) {};
                }

                this.onDrop(evt, this, cb);
            }.bind(this), false);
        }
    };

    /**
     * Event listener on click
     *
     * @private
     * @param evt
     */
    easyDD.prototype.onClick = function (evt) {
        this.addHiddenInput();
        this.hiddenInput.click();
    };

    /**
     * Set text on dropzone
     *
     * @public
     * @param text
     */
    easyDD.prototype.setText = function (text) {
        if (this.span == null) {
            this.span = document.createElement('span');
            this.span.classList.add('easyDDspan');
            this.context.appendChild(this.span);
        }

        this.span.innerHTML = text;
    };

    var easydd = {
        create: function (c) {
            return new easyDD(c);
        }
    };
    return easydd;
}();<|endoftext|>


<commit_before>'use strict';

var listenTo = [
  'keydown',
  'keyup',
  'keypress',
  'focus',
  'blur',
  'cut',
  'copy',
  'paste',
  'mousedown',
  'mouseup',
  'click',
  'compositionstart',
  'compositionupdate',
  'compositionend'
]

/**
 * Keys that cause the caret to move. Obviously most keys cause the
 * caret to move, but these are the ones that don’t produce keypresses.
 */
var selectionKeys = {
  8: 1,   // Backspace
  9: 1,   // Tab
  13: 1,  // Enter
  33: 1,  // Page up
  34: 1,  // Page down
  35: 1,  // End
  36: 1,  // Home
  37: 1,  // Left
  38: 1,  // Up
  39: 1,  // Right
  40: 1,  // Down
  46: 1   // Forward delete
}

function Events (Compose) {
  var debug = Compose.require('debug'),
      events = {},
      handlers,
      modKey

  handlers = listenTo.map(function (name) {
    var db = debug('compose:events:' + name)

    return {
      event: name,
      fn: function (e) {
        db(e)
        Compose.emit(name, e)
      }
    }
  })

  /**
   * modKey(e) determines if a platform's modifier key was pressed
   * during an event. Assumes Cmd for Mac, Ctrl otherwise.
   *
   * @param {Event} e
   * @return {Boolean}
   */
  modKey = events.modKey = function (e) {
    if (/Mac/.test(navigator.platform))
      return e.metaKey

    return e.ctrlKey
  }

  /**
   * enterKey(e) determines if the sequence of keys pressed during
   * an event would result in a carriage return. Known: enter key
   * and Ctrl+M.
   *
   * @param {Event} e
   * @return {Boolean}
   */
  events.enterKey = function (e) {
    return e.keyCode === 13 || (e.keyCode === 77 && e.ctrlKey)
  }

  events.backspace = function (e) {
    return e.keyCode === 8
  }

  events.forwardDelete = function (e) {
    return e.keyCode === 46
  }

  events.spacebar = function (e) {

    if (e.type === 'keypress') {
      return /[\u0020\u00A0\u180E\u2000-\u200B\u202F\u205F\u3000\uFEFF]/
        .test(String.fromCharCode(e.which))
    }

    return e.keyCode === 32
  }

  events.selectall = function (e) {
    return e.keyCode === 65 && modKey(e)
  }

  events.selectKey = function (e) {
    return !!selectionKeys[e.keyCode]
  }

  /**
   * pause() removes event listeners from the element, which stops the
   * emitting of events on Compose.
   */
  events.pause = function () {
    handlers.forEach(function (handler) {
      Compose.elem.removeEventListener(handler.event, handler.fn)
    })
  }

  /**
   * unpause() attaches event listeners to the element and emits those
   * events on Compose.
   */
  events.unpause = function () {
    handlers.forEach(function (handler) {
      Compose.elem.addEventListener(handler.event, handler.fn)
    })
  }

  events.disable = function () {
    this.pause()
  }

  events.unpause()
  Compose.provide('events', events)
}

module.exports = Events
<commit_msg>events: add IME-related method<commit_after>'use strict';

var listenTo = [
  'keydown',
  'keyup',
  'keypress',
  'focus',
  'blur',
  'cut',
  'copy',
  'paste',
  'mousedown',
  'mouseup',
  'click',
  'compositionstart',
  'compositionupdate',
  'compositionend'
]

/**
 * Keys that cause the caret to move. Obviously most keys cause the
 * caret to move, but these are the ones that don’t produce keypresses.
 */
var selectionKeys = {
  8: 1,   // Backspace
  9: 1,   // Tab
  13: 1,  // Enter
  33: 1,  // Page up
  34: 1,  // Page down
  35: 1,  // End
  36: 1,  // Home
  37: 1,  // Left
  38: 1,  // Up
  39: 1,  // Right
  40: 1,  // Down
  46: 1   // Forward delete
}

function Events (Compose) {
  var debug = Compose.require('debug'),
      events = {},
      handlers,
      modKey

  handlers = listenTo.map(function (name) {
    var db = debug('compose:events:' + name)

    return {
      event: name,
      fn: function (e) {
        db(e)
        Compose.emit(name, e)
      }
    }
  })

  /**
   * modKey(e) determines if a platform's modifier key was pressed
   * during an event. Assumes Cmd for Mac, Ctrl otherwise.
   *
   * @param {Event} e
   * @return {Boolean}
   */
  modKey = events.modKey = function (e) {
    if (/Mac/.test(navigator.platform))
      return e.metaKey

    return e.ctrlKey
  }

  /**
   * enterKey(e) determines if the sequence of keys pressed during
   * an event would result in a carriage return. Known: enter key
   * and Ctrl+M.
   *
   * @param {Event} e
   * @return {Boolean}
   */
  events.enterKey = function (e) {
    return e.keyCode === 13 || (e.keyCode === 77 && e.ctrlKey)
  }

  var composing = false
  Compose.on('compositionstart', function () {
    composing = true
  })
  Compose.on('compositionend', function () {
    composing = false
  })

  /**
   * composing() determines if IME composition is currently underway.
   *
   * @return {Boolean}
   */
  events.composing = function () {
    return composing
  }

  events.backspace = function (e) {
    return e.keyCode === 8
  }

  events.forwardDelete = function (e) {
    return e.keyCode === 46
  }

  events.spacebar = function (e) {

    if (e.type === 'keypress') {
      return /[\u0020\u00A0\u180E\u2000-\u200B\u202F\u205F\u3000\uFEFF]/
        .test(String.fromCharCode(e.which))
    }

    return e.keyCode === 32
  }

  events.selectall = function (e) {
    return e.keyCode === 65 && modKey(e)
  }

  events.selectKey = function (e) {
    return !!selectionKeys[e.keyCode]
  }

  /**
   * pause() removes event listeners from the element, which stops the
   * emitting of events on Compose.
   */
  events.pause = function () {
    handlers.forEach(function (handler) {
      Compose.elem.removeEventListener(handler.event, handler.fn)
    })
  }

  /**
   * unpause() attaches event listeners to the element and emits those
   * events on Compose.
   */
  events.unpause = function () {
    handlers.forEach(function (handler) {
      Compose.elem.addEventListener(handler.event, handler.fn)
    })
  }

  events.disable = function () {
    this.pause()
  }

  events.unpause()
  Compose.provide('events', events)
}

module.exports = Events
<|endoftext|>


<commit_before><commit_msg>Refactor markers<commit_after><|endoftext|>


<commit_before>/* eslint babel/semi: "error" */
import _ from 'lodash';

import { PubSub as pubSub } from '../models';

export function addModel(dbAdapter) {
  class Timeline {
    id;
    intId;
    userId;
    user;
    createdAt;
    updatedAt;
    offset;
    limit;
    currentUser;
    name_;

    constructor(params) {
      this.id = params.id;
      this.intId = params.intId;
      this.name = params.name;
      this.userId = params.userId;
      this.user = null;

      if (parseInt(params.createdAt, 10)) {
        this.createdAt = params.createdAt;
      }

      if (parseInt(params.updatedAt, 10)) {
        this.updatedAt = params.updatedAt;
      }

      this.offset = parseInt(params.offset, 10) || 0;
      this.limit = parseInt(params.limit, 10) || 30;
      this.currentUser = params.currentUser;
    }

    get name() {
      return this.name_;
    }

    set name(newValue) {
      if (!newValue) {
        this.name_ = '';
      }

      this.name_ = newValue.trim();
    }

    /**
     * Adds the specified post to all timelines where it needs to appear
     * (the timelines of the feeds to which it is posted, the River of News
     * timeline of the posting user and the River of News timelines of all
     * subscribers of the feeds to which it is posted).
     */
    async publishPost(post) {
      const currentTime = new Date().getTime();

      // We can use post.timelineIds here instead of post.getPostedToIds
      // because we are about to create that post and have just received
      // a request from user, so postedToIds == timelineIds here
      const timelines = await dbAdapter.getTimelinesByIds(post.timelineIds);

      const promises = timelines.map(async (timeline) => {
        const feed = await timeline.getUser();
        await feed.updateLastActivityAt();

        if (timeline.isDirects()) {
          await pubSub.updateUnreadDirects(timeline.userId);
        }

        return timeline.getSubscribersRiversOfNewsIntIds();
      });

      const allSubscribedTimelineIds = _.flatten(await Promise.all(promises));
      const allTimelines = _.uniq(_.union(post.feedIntIds, allSubscribedTimelineIds));
      await dbAdapter.setPostBumpedAt(post.id, currentTime);
      await dbAdapter.insertPostIntoFeeds(allTimelines, post.id);
      await pubSub.newPost(post.id);
    }

    static getObjectsByIds(objectIds) {
      return dbAdapter.getTimelinesByIds(objectIds);
    }

    validate() {
      const valid = this.name
        && this.name.length > 0
        && this.userId
        && this.userId.length > 0;

      if (!valid) {
        throw new Error('Invalid');
      }
    }

    create() {
      return this._createTimeline();
    }

    async _createTimeline() {
      const currentTime = new Date().getTime();

      await this.validate();

      const payload = {
        'name':      this.name,
        'userId':    this.userId,
        'createdAt': currentTime.toString(),
        'updatedAt': currentTime.toString()
      };

      const ids = await dbAdapter.createTimeline(payload);
      this.id = ids.id;
      this.intId = ids.intId;

      this.createdAt = currentTime;
      this.updatedAt = currentTime;

      return this;
    }

    async getPostIds(offset, limit) {
      if (_.isUndefined(offset)) {
        ({ offset } = this);
      } else if (offset < 0) {
        offset = 0;
      }

      // -1 = special magic number, meaning “do not use limit defaults,
      // do not use passed in value, use 0 instead". this is at the very least
      // used in Timeline.mergeTo()
      if (_.isUndefined(limit)) {
        ({ limit } = this);
      } else if (limit < 0) {
        limit = 0;
      }

      const valid = await this.canShow(this.currentUser);

      if (!valid) {
        return [];
      }

      this.postIds = await dbAdapter.getTimelinePostsRange(this.intId, offset, limit);
      return this.postIds;
    }

    async getFeedPosts(offset, limit, params, customFeedIds) {
      const valid = await this.canShow(this.currentUser);

      if (!valid) {
        return [];
      }

      let feedIds = [this.intId];
      if (customFeedIds) {
        feedIds = customFeedIds;
      }

      return dbAdapter.getFeedsPostsRange(feedIds, offset, limit, params);
    }

    async getPosts(offset, limit) {
      if (_.isUndefined(offset)) {
        ({ offset } = this);
      } else if (offset < 0) {
        offset = 0;
      }

      // -1 = special magic number, meaning “do not use limit defaults,
      // do not use passed in value, use 0 instead". this is at the very least
      // used in Timeline.mergeTo()
      if (_.isUndefined(limit)) {
        ({ limit } = this);
      } else if (limit < 0) {
        limit = 0;
      }

      const reader = this.currentUser ? (await dbAdapter.getUserById(this.currentUser)) : null;
      const banIds = reader ? (await reader.getBanIds()) : [];
      const readerOwnFeeds = reader ? (await reader.getPublicTimelinesIntIds()) : [];
      const feedOwner = await this.getUser();

      let posts;
      if (this.name !== 'MyDiscussions') {
        posts = await this.getFeedPosts(0, offset + limit, { currentUser: this.currentUser });
      } else {
        const myDiscussionsFeedSourcesIds = await Promise.all([feedOwner.getCommentsTimelineIntId(), feedOwner.getLikesTimelineIntId()]);
        posts = await this.getFeedPosts(0, offset + limit, { currentUser: this.currentUser }, myDiscussionsFeedSourcesIds);
      }
      const postIds = posts.map((p) => {
        return p.id;
      });

      if (reader && this.name === 'RiverOfNews') {
        let oldestPostTime;
        if (posts[posts.length - 1]) {
          oldestPostTime = posts[posts.length - 1].bumpedAt;
        }

        const localBumps = await dbAdapter.getUserLocalBumps(reader.id, oldestPostTime);
        const localBumpedPostIds = localBumps.map((bump) => bump.postId);

        const absentPostIds = _.difference(localBumpedPostIds, postIds);
        if (absentPostIds.length > 0) {
          let localBumpedPosts = await dbAdapter.getPostsByIds(absentPostIds, { currentUser: this.currentUser });
          localBumpedPosts = _.sortBy(localBumpedPosts, (post) => {
            return _.indexOf(absentPostIds, post.id);
          });
          posts = localBumpedPosts.concat(posts);
        }

        for (const p of posts) {
          if (localBumpedPostIds.includes(p.id)) {
            const bump = localBumps.find((b) => b.postId === p.id);
            p.locallyBumpedAt = bump.bumpedAt;
          }
        }
      }

      posts.sort((p1, p2) => {
        const t1 = Math.max(p1.locallyBumpedAt || 0, p1.bumpedAt);
        const t2 = Math.max(p2.locallyBumpedAt || 0, p2.bumpedAt);

        return t2 - t1;
      });

      posts = posts.slice(offset, offset + limit);

      const uids = _.uniq(posts.map((post) => post.userId));
      const users = (await dbAdapter.getUsersByIds(uids)).filter(Boolean);
      const readerUserId = this.currentUser;
      const banMatrix = await dbAdapter.getBanMatrixByUsersForPostReader(uids, readerUserId);

      const usersCache = {};

      for (let i = 0; i < users.length; i++) {
        const user = users[i];
        usersCache[user.id] = [user, banMatrix[i][1]];
      }

      async function userById(id) {
        if (!(id in usersCache)) {
          const user = await dbAdapter.getUserById(id);

          if (!user) {
            throw new Error(`no user for id=${id}`);
          }

          const bans = await user.getBanIds();
          const isReaderBanned = bans.includes(readerUserId);
          usersCache[id] = [user, isReaderBanned];
        }

        return usersCache[id];
      }

      posts = await Promise.all(posts.map(async (post) => {
        if (post.userId === this.currentUser) {
          // shortcut for the author
          return post;
        }

        let author, authorBannedReader;

        try {
          [author, authorBannedReader] = await userById(post.userId);
        } catch (e) {
          throw new Error(`did not find user-object of author of post with id=${post.id}\nPREVIOUS: ${e.message}`);
        }

        const readerBannedAuthor = banIds.includes(post.userId);

        if (readerBannedAuthor || authorBannedReader) {
          return null;
        }

        if (author.isPrivate) {
          if ((feedOwner.isPrivate !== '1' && this.isPosts()) || this.isDirects()) {
            return post;
          }

          if (_.intersection(post.destinationFeedIds, readerOwnFeeds).length > 0) {
            return post;
          }

          if (reader && _.intersection(post.destinationFeedIds, reader.subscribedFeedIds).length > 0) {
            return post;
          }

          if (!await post.canShow(this.currentUser, false)) {
            return null;
          }
        }

        return post;
      }));

      this.posts = posts.filter(Boolean);

      return this.posts;
    }

    async unmerge(feedIntId) {
      const postIds = await dbAdapter.getTimelinesIntersectionPostIds(this.intId, feedIntId);

      await Promise.all(_.flatten(postIds.map((postId) =>
        dbAdapter.withdrawPostFromFeeds([feedIntId], postId)
      )));

      return;
    }

    async getUser() {
      if (!this.user) {
        this.user = await dbAdapter.getFeedOwnerById(this.userId);
      }

      return this.user;
    }

    /**
     * Returns the IDs of users subscribed to this timeline, as a promise.
     */
    async getSubscriberIds(includeSelf) {
      let userIds = await dbAdapter.getTimelineSubscribersIds(this.id);

      // A user is always subscribed to their own posts timeline.
      if (includeSelf && (this.isPosts() || this.isDirects())) {
        userIds = _.uniq(userIds.concat([this.userId]));
      }

      this.subscriberIds = userIds;

      return userIds;
    }

    async getSubscribers(includeSelf) {
      let users = await dbAdapter.getTimelineSubscribers(this.intId);

      if (includeSelf && (this.isPosts() || this.isDirects())) {
        const currentUser = await dbAdapter.getUserById(this.userId);
        users = users.concat(currentUser);
      }

      this.subscribers = users;

      return this.subscribers;
    }

    async loadVisibleSubscribersAndAdmins(feedOwner, viewer) {
      if (!feedOwner || feedOwner.id != this.userId) {
        throw new Error('Wrong feed owner');
      }

      const feedOwnerSubscriberIds = await feedOwner.getSubscriberIds();

      if (feedOwner.isPrivate !== '1') {
        return;
      }

      if (viewer && (viewer.id == feedOwner.id || feedOwnerSubscriberIds.includes(viewer.id))) {
        return;
      }

      feedOwner.administrators = [];
      this.subscribers = [];
      this.user = feedOwner;
    }

    /**
     * Returns the list of the 'River of News' timelines of all subscribers to this
     * timeline.
     */
    async getSubscribedTimelineIds() {
      const subscribers = await this.getSubscribers(true);
      return await Promise.all(subscribers.map((subscriber) => subscriber.getRiverOfNewsTimelineId()));
    }

    async getSubscribersRiversOfNewsIntIds() {
      const subscribers = await this.getSubscribers(true);
      return await Promise.all(subscribers.map((subscriber) => subscriber.getRiverOfNewsTimelineIntId()));
    }

    isRiverOfNews() {
      return this.name === 'RiverOfNews';
    }

    isPosts() {
      return this.name === 'Posts';
    }

    isLikes() {
      return this.name === 'Likes';
    }

    isComments() {
      return this.name === 'Comments';
    }

    isDirects() {
      return this.name === 'Directs';
    }

    isHides() {
      return this.name === 'Hides';
    }

    /**
     * Personal timeline can be viewed only by its owner
     * @return {boolean}
     */
    isPersonal() {
      return this.name === 'RiverOfNews' ||
        this.name === 'Directs' ||
        this.name === 'Hides' ||
        this.name === 'MyDiscussions';
    }

    async canShow(readerId) {
      if (this.userId === readerId) {
        return true;  // owner can read her posts
      }

      if (this.isDirects()) {
        return false;  // this is someone else's direct
      }

      const user = await this.getUser();

      if (!user) {
        throw new Error;
      }

      // this feed is not visible to anonymous and we just happen to be one
      if (!readerId && user.isProtected === '1') {
        return false;
      }

      if (user.isPrivate === '1') {
        // user can view post if and only if she is subscriber
        const subscriberIds = await this.getSubscriberIds();
        return subscriberIds.includes(readerId);
      }

      // this is a public feed, anyone can read public posts, this is a free country
      return true;
    }
  }

  return Timeline;
}
<commit_msg>✏Add missed "static" qualifier<commit_after>/* eslint babel/semi: "error" */
import _ from 'lodash';

import { PubSub as pubSub } from '../models';

export function addModel(dbAdapter) {
  class Timeline {
    id;
    intId;
    userId;
    user;
    createdAt;
    updatedAt;
    offset;
    limit;
    currentUser;
    name_;

    constructor(params) {
      this.id = params.id;
      this.intId = params.intId;
      this.name = params.name;
      this.userId = params.userId;
      this.user = null;

      if (parseInt(params.createdAt, 10)) {
        this.createdAt = params.createdAt;
      }

      if (parseInt(params.updatedAt, 10)) {
        this.updatedAt = params.updatedAt;
      }

      this.offset = parseInt(params.offset, 10) || 0;
      this.limit = parseInt(params.limit, 10) || 30;
      this.currentUser = params.currentUser;
    }

    get name() {
      return this.name_;
    }

    set name(newValue) {
      if (!newValue) {
        this.name_ = '';
      }

      this.name_ = newValue.trim();
    }

    /**
     * Adds the specified post to all timelines where it needs to appear
     * (the timelines of the feeds to which it is posted, the River of News
     * timeline of the posting user and the River of News timelines of all
     * subscribers of the feeds to which it is posted).
     */
    static async publishPost(post) {
      const currentTime = new Date().getTime();

      // We can use post.timelineIds here instead of post.getPostedToIds
      // because we are about to create that post and have just received
      // a request from user, so postedToIds == timelineIds here
      const timelines = await dbAdapter.getTimelinesByIds(post.timelineIds);

      const promises = timelines.map(async (timeline) => {
        const feed = await timeline.getUser();
        await feed.updateLastActivityAt();

        if (timeline.isDirects()) {
          await pubSub.updateUnreadDirects(timeline.userId);
        }

        return timeline.getSubscribersRiversOfNewsIntIds();
      });

      const allSubscribedTimelineIds = _.flatten(await Promise.all(promises));
      const allTimelines = _.uniq(_.union(post.feedIntIds, allSubscribedTimelineIds));
      await dbAdapter.setPostBumpedAt(post.id, currentTime);
      await dbAdapter.insertPostIntoFeeds(allTimelines, post.id);
      await pubSub.newPost(post.id);
    }

    static getObjectsByIds(objectIds) {
      return dbAdapter.getTimelinesByIds(objectIds);
    }

    validate() {
      const valid = this.name
        && this.name.length > 0
        && this.userId
        && this.userId.length > 0;

      if (!valid) {
        throw new Error('Invalid');
      }
    }

    create() {
      return this._createTimeline();
    }

    async _createTimeline() {
      const currentTime = new Date().getTime();

      await this.validate();

      const payload = {
        'name':      this.name,
        'userId':    this.userId,
        'createdAt': currentTime.toString(),
        'updatedAt': currentTime.toString()
      };

      const ids = await dbAdapter.createTimeline(payload);
      this.id = ids.id;
      this.intId = ids.intId;

      this.createdAt = currentTime;
      this.updatedAt = currentTime;

      return this;
    }

    async getPostIds(offset, limit) {
      if (_.isUndefined(offset)) {
        ({ offset } = this);
      } else if (offset < 0) {
        offset = 0;
      }

      // -1 = special magic number, meaning “do not use limit defaults,
      // do not use passed in value, use 0 instead". this is at the very least
      // used in Timeline.mergeTo()
      if (_.isUndefined(limit)) {
        ({ limit } = this);
      } else if (limit < 0) {
        limit = 0;
      }

      const valid = await this.canShow(this.currentUser);

      if (!valid) {
        return [];
      }

      this.postIds = await dbAdapter.getTimelinePostsRange(this.intId, offset, limit);
      return this.postIds;
    }

    async getFeedPosts(offset, limit, params, customFeedIds) {
      const valid = await this.canShow(this.currentUser);

      if (!valid) {
        return [];
      }

      let feedIds = [this.intId];
      if (customFeedIds) {
        feedIds = customFeedIds;
      }

      return dbAdapter.getFeedsPostsRange(feedIds, offset, limit, params);
    }

    async getPosts(offset, limit) {
      if (_.isUndefined(offset)) {
        ({ offset } = this);
      } else if (offset < 0) {
        offset = 0;
      }

      // -1 = special magic number, meaning “do not use limit defaults,
      // do not use passed in value, use 0 instead". this is at the very least
      // used in Timeline.mergeTo()
      if (_.isUndefined(limit)) {
        ({ limit } = this);
      } else if (limit < 0) {
        limit = 0;
      }

      const reader = this.currentUser ? (await dbAdapter.getUserById(this.currentUser)) : null;
      const banIds = reader ? (await reader.getBanIds()) : [];
      const readerOwnFeeds = reader ? (await reader.getPublicTimelinesIntIds()) : [];
      const feedOwner = await this.getUser();

      let posts;
      if (this.name !== 'MyDiscussions') {
        posts = await this.getFeedPosts(0, offset + limit, { currentUser: this.currentUser });
      } else {
        const myDiscussionsFeedSourcesIds = await Promise.all([feedOwner.getCommentsTimelineIntId(), feedOwner.getLikesTimelineIntId()]);
        posts = await this.getFeedPosts(0, offset + limit, { currentUser: this.currentUser }, myDiscussionsFeedSourcesIds);
      }
      const postIds = posts.map((p) => {
        return p.id;
      });

      if (reader && this.name === 'RiverOfNews') {
        let oldestPostTime;
        if (posts[posts.length - 1]) {
          oldestPostTime = posts[posts.length - 1].bumpedAt;
        }

        const localBumps = await dbAdapter.getUserLocalBumps(reader.id, oldestPostTime);
        const localBumpedPostIds = localBumps.map((bump) => bump.postId);

        const absentPostIds = _.difference(localBumpedPostIds, postIds);
        if (absentPostIds.length > 0) {
          let localBumpedPosts = await dbAdapter.getPostsByIds(absentPostIds, { currentUser: this.currentUser });
          localBumpedPosts = _.sortBy(localBumpedPosts, (post) => {
            return _.indexOf(absentPostIds, post.id);
          });
          posts = localBumpedPosts.concat(posts);
        }

        for (const p of posts) {
          if (localBumpedPostIds.includes(p.id)) {
            const bump = localBumps.find((b) => b.postId === p.id);
            p.locallyBumpedAt = bump.bumpedAt;
          }
        }
      }

      posts.sort((p1, p2) => {
        const t1 = Math.max(p1.locallyBumpedAt || 0, p1.bumpedAt);
        const t2 = Math.max(p2.locallyBumpedAt || 0, p2.bumpedAt);

        return t2 - t1;
      });

      posts = posts.slice(offset, offset + limit);

      const uids = _.uniq(posts.map((post) => post.userId));
      const users = (await dbAdapter.getUsersByIds(uids)).filter(Boolean);
      const readerUserId = this.currentUser;
      const banMatrix = await dbAdapter.getBanMatrixByUsersForPostReader(uids, readerUserId);

      const usersCache = {};

      for (let i = 0; i < users.length; i++) {
        const user = users[i];
        usersCache[user.id] = [user, banMatrix[i][1]];
      }

      async function userById(id) {
        if (!(id in usersCache)) {
          const user = await dbAdapter.getUserById(id);

          if (!user) {
            throw new Error(`no user for id=${id}`);
          }

          const bans = await user.getBanIds();
          const isReaderBanned = bans.includes(readerUserId);
          usersCache[id] = [user, isReaderBanned];
        }

        return usersCache[id];
      }

      posts = await Promise.all(posts.map(async (post) => {
        if (post.userId === this.currentUser) {
          // shortcut for the author
          return post;
        }

        let author, authorBannedReader;

        try {
          [author, authorBannedReader] = await userById(post.userId);
        } catch (e) {
          throw new Error(`did not find user-object of author of post with id=${post.id}\nPREVIOUS: ${e.message}`);
        }

        const readerBannedAuthor = banIds.includes(post.userId);

        if (readerBannedAuthor || authorBannedReader) {
          return null;
        }

        if (author.isPrivate) {
          if ((feedOwner.isPrivate !== '1' && this.isPosts()) || this.isDirects()) {
            return post;
          }

          if (_.intersection(post.destinationFeedIds, readerOwnFeeds).length > 0) {
            return post;
          }

          if (reader && _.intersection(post.destinationFeedIds, reader.subscribedFeedIds).length > 0) {
            return post;
          }

          if (!await post.canShow(this.currentUser, false)) {
            return null;
          }
        }

        return post;
      }));

      this.posts = posts.filter(Boolean);

      return this.posts;
    }

    async unmerge(feedIntId) {
      const postIds = await dbAdapter.getTimelinesIntersectionPostIds(this.intId, feedIntId);

      await Promise.all(_.flatten(postIds.map((postId) =>
        dbAdapter.withdrawPostFromFeeds([feedIntId], postId)
      )));

      return;
    }

    async getUser() {
      if (!this.user) {
        this.user = await dbAdapter.getFeedOwnerById(this.userId);
      }

      return this.user;
    }

    /**
     * Returns the IDs of users subscribed to this timeline, as a promise.
     */
    async getSubscriberIds(includeSelf) {
      let userIds = await dbAdapter.getTimelineSubscribersIds(this.id);

      // A user is always subscribed to their own posts timeline.
      if (includeSelf && (this.isPosts() || this.isDirects())) {
        userIds = _.uniq(userIds.concat([this.userId]));
      }

      this.subscriberIds = userIds;

      return userIds;
    }

    async getSubscribers(includeSelf) {
      let users = await dbAdapter.getTimelineSubscribers(this.intId);

      if (includeSelf && (this.isPosts() || this.isDirects())) {
        const currentUser = await dbAdapter.getUserById(this.userId);
        users = users.concat(currentUser);
      }

      this.subscribers = users;

      return this.subscribers;
    }

    async loadVisibleSubscribersAndAdmins(feedOwner, viewer) {
      if (!feedOwner || feedOwner.id != this.userId) {
        throw new Error('Wrong feed owner');
      }

      const feedOwnerSubscriberIds = await feedOwner.getSubscriberIds();

      if (feedOwner.isPrivate !== '1') {
        return;
      }

      if (viewer && (viewer.id == feedOwner.id || feedOwnerSubscriberIds.includes(viewer.id))) {
        return;
      }

      feedOwner.administrators = [];
      this.subscribers = [];
      this.user = feedOwner;
    }

    /**
     * Returns the list of the 'River of News' timelines of all subscribers to this
     * timeline.
     */
    async getSubscribedTimelineIds() {
      const subscribers = await this.getSubscribers(true);
      return await Promise.all(subscribers.map((subscriber) => subscriber.getRiverOfNewsTimelineId()));
    }

    async getSubscribersRiversOfNewsIntIds() {
      const subscribers = await this.getSubscribers(true);
      return await Promise.all(subscribers.map((subscriber) => subscriber.getRiverOfNewsTimelineIntId()));
    }

    isRiverOfNews() {
      return this.name === 'RiverOfNews';
    }

    isPosts() {
      return this.name === 'Posts';
    }

    isLikes() {
      return this.name === 'Likes';
    }

    isComments() {
      return this.name === 'Comments';
    }

    isDirects() {
      return this.name === 'Directs';
    }

    isHides() {
      return this.name === 'Hides';
    }

    /**
     * Personal timeline can be viewed only by its owner
     * @return {boolean}
     */
    isPersonal() {
      return this.name === 'RiverOfNews' ||
        this.name === 'Directs' ||
        this.name === 'Hides' ||
        this.name === 'MyDiscussions';
    }

    async canShow(readerId) {
      if (this.userId === readerId) {
        return true;  // owner can read her posts
      }

      if (this.isDirects()) {
        return false;  // this is someone else's direct
      }

      const user = await this.getUser();

      if (!user) {
        throw new Error;
      }

      // this feed is not visible to anonymous and we just happen to be one
      if (!readerId && user.isProtected === '1') {
        return false;
      }

      if (user.isPrivate === '1') {
        // user can view post if and only if she is subscriber
        const subscriberIds = await this.getSubscriberIds();
        return subscriberIds.includes(readerId);
      }

      // this is a public feed, anyone can read public posts, this is a free country
      return true;
    }
  }

  return Timeline;
}
<|endoftext|>


<commit_before>/*global
 define
 */
/*jslint
 browser: true,
 white: true
 */
define([], function () {
    'use strict';
    function createElement(name) {
        return document.createElement(name);
    }
    function appendRoot(child) {
        document.appendChild(child);
    }
    function append(parent, child) {
        return parent.appendChild(child);
    }
    function remove(parent, child) {
        return parent.removeChild(child);
    }
    function findById(id) {
        return document.getElementById(id);
    }
    function nodeForId(id) {
        return document.getElementById(id);
    }
    function qs(selector) {
        return document.querySelector(selector);
    }
    function qsa(selector) {
        var result = document.querySelectorAll(selector);
        if (result === null) {
            return [];
        }
        return Array.prototype.slice.call(result);
    }
    function setHtml(nodeOrSelector, content) {
        var node;
        if (typeof nodeOrSelector === 'string') {
            node = qs(nodeOrSelector);
            if (node === null) {
                throw new Error('No node found for selector "' + nodeOrSelector + '"');
            }
        } else {
            node = nodeOrSelector;
        }
        node.innerHTML = content;
        return node;
    }

    return {
        createElement: createElement,
        appendRoot: appendRoot,
        append: append,
        remove: remove,
        setHTML: setHtml,
        setHtml: setHtml,
        findById: findById,
        nodeForId: nodeForId,
        getById: findById,
        qs: qs,
        qsa: qsa
    };
});<commit_msg>qs and qsa have optional node scope (as first argument)<commit_after>/*global
 define
 */
/*jslint
 browser: true,
 white: true
 */
define([], function () {
    'use strict';
    function createElement(name) {
        return document.createElement(name);
    }
    function appendRoot(child) {
        document.appendChild(child);
    }
    function append(parent, child) {
        return parent.appendChild(child);
    }
    function remove(parent, child) {
        return parent.removeChild(child);
    }
    function findById(id) {
        return document.getElementById(id);
    }
    function nodeForId(id) {
        return document.getElementById(id);
    }
    function qs(node, selector) {
        if (selector === undefined) {
            selector = node;
            node = document;
        }
        return node.querySelector(selector);
    }
    function qsa(node, selector) {
        if (selector === undefined) {
            selector = node;
            node = document;
        }
        var result = node.querySelectorAll(selector);
        if (result === null) {
            return [];
        }
        return Array.prototype.slice.call(result);
    }
    function setHtml(nodeOrSelector, content) {
        var node;
        if (typeof nodeOrSelector === 'string') {
            node = qs(nodeOrSelector);
            if (node === null) {
                throw new Error('No node found for selector "' + nodeOrSelector + '"');
            }
        } else {
            node = nodeOrSelector;
        }
        node.innerHTML = content;
        return node;
    }

    return {
        createElement: createElement,
        appendRoot: appendRoot,
        append: append,
        remove: remove,
        setHTML: setHtml,
        setHtml: setHtml,
        findById: findById,
        nodeForId: nodeForId,
        getById: findById,
        qs: qs,
        qsa: qsa
    };
});<|endoftext|>


<commit_before>var models = require('../models')
  , async = require('async')
  , redis = require('redis')
  , _ = require('underscore')
  , uuid = require('node-uuid')

exports.addModel = function(db) {
  function Timeline(params) {
    this.id = params.id
    this.name = params.name
    this.userId = params.userId

    this.start = parseInt(params.start, 10) || 0
    this.num = parseInt(params.num, 10) || 25
  }

  Timeline.getAttributes = function() {
    return ['id', 'user', 'posts']
  }

  Timeline.findById = function(timelineId, params, callback) {
    db.hgetall('timeline:' + timelineId, function(err, attrs) {
      if (!attrs || err)
        return callback(1, null)

      attrs.id = timelineId
      attrs['start'] = params['start']
      attrs['num'] = params['num']

      callback(err, new Timeline(attrs))
    })
  }

  Timeline.updatePost = function(timelineId, postId, callback) {
    var currentTime = new Date().getTime()
    db.zadd('timeline:' + timelineId + ':posts', currentTime, postId, function(err, res) {
      db.sadd('post:' + postId + ':timelines', timelineId, function(err, res) {
        db.hset('post:' + postId, 'updatedAt', currentTime, function(err, res) {
          callback(err, res)
        })
      })
    })
  }

  Timeline.getEveryoneTimeline = function(params, callback) {
    Timeline.getEveryoneTimelineId(function(err, timelineId) {
      Timeline.findById(timelineId, params, function(err, timeline) {
        return callback(err, timeline)
      })
    })
  }

  Timeline.getEveryoneTimelineId = function(callback) {
    var everyoneId = 'everyone'
    db.exists('timeline:' + everyoneId, function(err, res) {
      if (res === 1) return callback(err, everyoneId)

      db.hmset('timeline:' + everyoneId,
        { 'name': 'Posts',
          'userId': ''
        }, function(err, res) {
          callback(err, everyoneId)
        })
    })
  }

  Timeline.newPost = function(postId, callback) {
    var currentTime = new Date().getTime()

    models.Post.findById(postId, function(err, post) {
      post.getSubscribedTimelinesIds(function(err, timelinesIds) {
        //we add everyoneTimelineId to timelineIds, and newPost will be put in everyone timeline
        Timeline.getEveryoneTimelineId(function(err, everyoneTimelineId) {
          timelinesIds.push(everyoneTimelineId)

          var pub = redis.createClient();

          async.forEach(timelinesIds, function(timelineId, callback) {
            db.zadd('timeline:' + timelineId + ':posts', currentTime, postId, function(err, res) {
              db.hset('post:' + postId, 'updatedAt', currentTime, function(err, res) {
                db.sadd('post:' + postId + ':timelines', timelineId, function(err, res) {
                  pub.publish('newPost', JSON.stringify({ postId: postId,
                    timelineId: timelineId }))

                  callback(err)
                })
              })
            })
          }, function(err) {
            callback(err)
          })
        })
      })
    })
  }

  Timeline.prototype = {
    getSubscribersIds: function(callback) {
      if (this.subscribersIds) {
        callback(null, this.subscribersIds)
      } else {
        var that = this
        db.zrevrange('timeline:' + this.id + ':subscribers', 0, -1, function(err, subscribersIds) {
          that.subscribersIds = subscribersIds || []
          callback(err, that.subscribersIds)
        })
      }
    },

    getSubscribers: function(callback) {
      if (this.subscribers) {
        callback(null, this.subscribers)
      } else {
        var that = this
        this.getSubscribersIds(function(err, subscribersIds) {
          async.map(Object.keys(subscribersIds), function(subscriberId, callback) {
            models.User.findById(subscribersIds[subscriberId], function(err, subscriber) {
              callback(err, subscriber)
            })
          }, function(err, subscribers) {
            that.subscribers = subscribers.compact()
            callback(err, that.subscribers)
          })
        })
      }
    },

    getPostsIds: function(start, num, callback) {
      if (this.postsIds) {
        callback(null, this.postsIds)
      } else {
        var that = this
        db.zrevrange('timeline:' + this.id + ':posts', start, start+num-1, function(err, postsIds) {
          that.postsIds = postsIds || []
          callback(err, that.postsIds)
        })
      }
    },

    getPosts: function(start, num, callback) {
      if (this.posts) {
        callback(null, this.posts)
      } else {
        var that = this
        this.getPostsIds(start, num, function(err, postsIds) {
          async.map(postsIds, function(postId, callback) {
            models.Post.findById(postId, function(err, post) {
              callback(err, post)
            })
          }, function(err, posts) {
            that.posts = posts
            callback(err, that.posts)
          })
        })
      }
    },

    // Not used
    getUsersIds: function(callback) {
      if (this.usersIds) {
        callback(null, this.usersIds)
      } else {
        var that = this;
        db.lrange('timeline:' + this.id + ':users', 0, -1, function(err, usersIds) {
          that.usersIds = usersIds || []
          callback(err, that.usersIds)
        })
      }
    },

    // Not used
    getUsers: function(callback) {
      if (this.users) {
        callback(null, this.users)
      } else {
        var that = this
        this.getUsersIds(function(err, usersIds) {
          async.map(usersIds, function(userId, callback) {
            models.User.findById(userId, function(err, user) {
              callback(err, user)
            })
          }, function(err, users) {
            that.users = users
            callback(err, that.users)
          })
        })
      }
    },

    getPostsCount: function(callback) {
      db.zcount('timeline:' + this.id + ':posts', '-inf', '+inf', function(err, res){
        callback(err, res)
      })
    },

    toJSON: function(params, callback) {
      var that = this
        , json = {}
        , select = params['select'] ||
            models.Timeline.getAttributes()

      if (select.indexOf('id') != -1)
        json.id = that.id

      if (select.indexOf('name') != -1)
        json.name = that.name

      if (select.indexOf('userId') != -1)
        json.userId = that.userId

      if (select.indexOf('user') != -1) {
        models.User.findById(that.userId, function(err, user) {
          user.toJSON(params.user || {}, function(err, userJSON) {
            json.user = userJSON

            if (select.indexOf('posts') != -1) {
              that.getPosts(that.start, that.num, function(err, posts) {
                async.map(posts, function(post, callback) {
                  post.toJSON(params.posts || {}, function(err, json) {
                    callback(err, json)
                  })
                }, function(err, postsJSON) {
                  json.posts = postsJSON

                  if (select.indexOf('subscribers') != -1) {
                    that.getSubscribers(function(err, subscribers) {
                      async.map(subscribers, function(subscriber, callback) {
                        subscriber.toJSON(params.subscribers || {}, function(err, json) {
                          callback(err, json)
                        })
                      }, function(err, subscribersJSON) {
                        json.subscribers = subscribersJSON

                        callback(err, json)
                      })
                    })
                  } else {
                    callback(err, json)
                  }
                })
              })
            } else {
              callback(err, json)
            }
          })
        })
      } else {
        callback(null, json)
      }
    }
  }

  return Timeline;

}
<commit_msg>Removes userId attribute in everyone timeline.<commit_after>var models = require('../models')
  , async = require('async')
  , redis = require('redis')
  , _ = require('underscore')
  , uuid = require('node-uuid')

exports.addModel = function(db) {
  function Timeline(params) {
    this.id = params.id
    this.name = params.name
    this.userId = params.userId

    this.start = parseInt(params.start, 10) || 0
    this.num = parseInt(params.num, 10) || 25
  }

  Timeline.getAttributes = function() {
    return ['id', 'user', 'posts']
  }

  Timeline.findById = function(timelineId, params, callback) {
    db.hgetall('timeline:' + timelineId, function(err, attrs) {
      if (!attrs || err)
        return callback(1, null)

      attrs.id = timelineId
      attrs['start'] = params['start']
      attrs['num'] = params['num']

      callback(err, new Timeline(attrs))
    })
  }

  Timeline.updatePost = function(timelineId, postId, callback) {
    var currentTime = new Date().getTime()
    db.zadd('timeline:' + timelineId + ':posts', currentTime, postId, function(err, res) {
      db.sadd('post:' + postId + ':timelines', timelineId, function(err, res) {
        db.hset('post:' + postId, 'updatedAt', currentTime, function(err, res) {
          callback(err, res)
        })
      })
    })
  }

  Timeline.getEveryoneTimeline = function(params, callback) {
    Timeline.getEveryoneTimelineId(function(err, timelineId) {
      Timeline.findById(timelineId, params, function(err, timeline) {
        return callback(err, timeline)
      })
    })
  }

  Timeline.getEveryoneTimelineId = function(callback) {
    var everyoneId = 'everyone'
    db.exists('timeline:' + everyoneId, function(err, res) {
      if (res === 1) return callback(err, everyoneId)

      db.hmset('timeline:' + everyoneId,
                 { 'name': 'Posts' }
               , function(err, res) {
                 callback(err, everyoneId)
               })
    })
  }

  Timeline.newPost = function(postId, callback) {
    var currentTime = new Date().getTime()

    models.Post.findById(postId, function(err, post) {
      post.getSubscribedTimelinesIds(function(err, timelinesIds) {
        // we add everyoneTimelineId to timelineIds, and newPost will
        // be put in everyone timeline
        Timeline.getEveryoneTimelineId(function(err, everyoneTimelineId) {
          timelinesIds.push(everyoneTimelineId)

          var pub = redis.createClient();

          async.forEach(timelinesIds, function(timelineId, callback) {
            db.zadd('timeline:' + timelineId + ':posts', currentTime, postId, function(err, res) {
              db.hset('post:' + postId, 'updatedAt', currentTime, function(err, res) {
                db.sadd('post:' + postId + ':timelines', timelineId, function(err, res) {
                  pub.publish('newPost', JSON.stringify({ postId: postId,
                    timelineId: timelineId }))

                  callback(err)
                })
              })
            })
          }, function(err) {
            callback(err)
          })
        })
      })
    })
  }

  Timeline.prototype = {
    getSubscribersIds: function(callback) {
      if (this.subscribersIds) {
        callback(null, this.subscribersIds)
      } else {
        var that = this
        db.zrevrange('timeline:' + this.id + ':subscribers', 0, -1, function(err, subscribersIds) {
          that.subscribersIds = subscribersIds || []
          callback(err, that.subscribersIds)
        })
      }
    },

    getSubscribers: function(callback) {
      if (this.subscribers) {
        callback(null, this.subscribers)
      } else {
        var that = this
        this.getSubscribersIds(function(err, subscribersIds) {
          async.map(Object.keys(subscribersIds), function(subscriberId, callback) {
            models.User.findById(subscribersIds[subscriberId], function(err, subscriber) {
              callback(err, subscriber)
            })
          }, function(err, subscribers) {
            that.subscribers = subscribers.compact()
            callback(err, that.subscribers)
          })
        })
      }
    },

    getPostsIds: function(start, num, callback) {
      if (this.postsIds) {
        callback(null, this.postsIds)
      } else {
        var that = this
        db.zrevrange('timeline:' + this.id + ':posts', start, start+num-1, function(err, postsIds) {
          that.postsIds = postsIds || []
          callback(err, that.postsIds)
        })
      }
    },

    getPosts: function(start, num, callback) {
      if (this.posts) {
        callback(null, this.posts)
      } else {
        var that = this
        this.getPostsIds(start, num, function(err, postsIds) {
          async.map(postsIds, function(postId, callback) {
            models.Post.findById(postId, function(err, post) {
              callback(err, post)
            })
          }, function(err, posts) {
            that.posts = posts
            callback(err, that.posts)
          })
        })
      }
    },

    // Not used
    getUsersIds: function(callback) {
      if (this.usersIds) {
        callback(null, this.usersIds)
      } else {
        var that = this;
        db.lrange('timeline:' + this.id + ':users', 0, -1, function(err, usersIds) {
          that.usersIds = usersIds || []
          callback(err, that.usersIds)
        })
      }
    },

    // Not used
    getUsers: function(callback) {
      if (this.users) {
        callback(null, this.users)
      } else {
        var that = this
        this.getUsersIds(function(err, usersIds) {
          async.map(usersIds, function(userId, callback) {
            models.User.findById(userId, function(err, user) {
              callback(err, user)
            })
          }, function(err, users) {
            that.users = users
            callback(err, that.users)
          })
        })
      }
    },

    getPostsCount: function(callback) {
      db.zcount('timeline:' + this.id + ':posts', '-inf', '+inf', function(err, res){
        callback(err, res)
      })
    },

    toJSON: function(params, callback) {
      var that = this
        , json = {}
        , select = params['select'] ||
            models.Timeline.getAttributes()

      if (select.indexOf('id') != -1)
        json.id = that.id

      if (select.indexOf('name') != -1)
        json.name = that.name

      if (select.indexOf('userId') != -1)
        json.userId = that.userId

      if (select.indexOf('user') != -1) {
        models.User.findById(that.userId, function(err, user) {
          user.toJSON(params.user || {}, function(err, userJSON) {
            json.user = userJSON

            if (select.indexOf('posts') != -1) {
              that.getPosts(that.start, that.num, function(err, posts) {
                async.map(posts, function(post, callback) {
                  post.toJSON(params.posts || {}, function(err, json) {
                    callback(err, json)
                  })
                }, function(err, postsJSON) {
                  json.posts = postsJSON

                  if (select.indexOf('subscribers') != -1) {
                    that.getSubscribers(function(err, subscribers) {
                      async.map(subscribers, function(subscriber, callback) {
                        subscriber.toJSON(params.subscribers || {}, function(err, json) {
                          callback(err, json)
                        })
                      }, function(err, subscribersJSON) {
                        json.subscribers = subscribersJSON

                        callback(err, json)
                      })
                    })
                  } else {
                    callback(err, json)
                  }
                })
              })
            } else {
              callback(err, json)
            }
          })
        })
      } else {
        callback(null, json)
      }
    }
  }

  return Timeline;

}
<|endoftext|>