<commit_before>define([
	"dojo/_base/kernel",
	"dojo/_base/config",
	"dojo/_base/declare",
	"dijit/registry",	// registry.getEnclosingWidget
	"dijit/_Contained",
	"dijit/_WidgetBase",
	"./TransitionEvent",
	"./View"
], function(kernel, config, declare, registry, Contained, WidgetBase, TransitionEvent, View){

/*=====
	var Contained = dijit._Contained;
	var WidgetBase = dijit._WidgetBase;
	var TransitionEvent = dojox.mobile.TransitionEvent;
	var View = dojox.mobile.View;
=====*/

	// module:
	//		dojox/mobile/_ItemBase
	// summary:
	//		A base class for item classes (e.g. ListItem, IconItem, etc.)

	return declare("dojox.mobile._ItemBase", [WidgetBase, Contained],{
		// summary:
		//		A base class for item classes (e.g. ListItem, IconItem, etc.)
		// description:
		//		_ItemBase is a base class for widgets that have capability to
		//		make a view transition when clicked.

		// icon: String
		//		An icon image to display. The value can be either a path for an
		//		image file or a class name of a DOM button. If icon is not
		//		specified, the iconBase parameter of the parent widget is used.
		icon: "",

		// iconPos: String
		//		The position of an aggregated icon. IconPos is comma separated
		//		values like top,left,width,height (ex. "0,0,29,29"). If iconPos
		//		is not specified, the iconPos parameter of the parent widget is
		//		used.
		iconPos: "", // top,left,width,height (ex. "0,0,29,29")

		// alt: String
		//		An alt text for the icon image.
		alt: "",

		// href: String
		//		A URL of another web page to go to.
		href: "",

		// hrefTarget: String
		//		A target that specifies where to open a page specified by
		//		href. The value will be passed to the 2nd argument of
		//		window.open().
		hrefTarget: "",

		// moveTo: String
		//		The id of the transition destination view which resides in the
		//		current page.
		//
		//		If the value has a hash sign ('#') before the id (e.g. #view1)
		//		and the dojo.hash module is loaded by the user application, the
		//		view transition updates the hash in the browser URL so that the
		//		user can bookmark the destination view. In this case, the user
		//		can also use the browser's back/forward button to navigate
		//		through the views in the browser history.
		//
		//		If null, transitions to a blank view.
		//		If '#', returns immediately without transition.
		moveTo: "",

		// scene: String
		//		The name of a scene. Used from dojox.mobile.app.
		scene: "",

		// clickable: Boolean
		//		If true, this item becomes clickable even if a transition
		//		destination (moveTo, etc.) is not specified.
		clickable: false,

		// url: String
		//		A URL of an html fragment page or JSON data that represents a
		//		new view content. The view content is loaded with XHR and
		//		inserted in the current page. Then a view transition occurs to
		//		the newly created view. The view is cached so that subsequent
		//		requests would not load the content again.
		url: "",

		// urlTarget: String
		//		Node id under which a new view will be created according to the
		//		url parameter. If not specified, The new view will be created as
		//		a sibling of the current view.
		urlTarget: "",

		// transition: String
		//		A type of animated transition effect. You can choose from the
		//		standard transition types, "slide", "fade", "flip", or from the
		//		extended transition types, "cover", "coverv", "dissolve",
		//		"reveal", "revealv", "scaleIn", "scaleOut", "slidev",
		//		"swirl", "zoomIn", "zoomOut". If "none" is specified, transition
		//		occurs immediately without animation.
		transition: "",

		// transitionDir: Number
		//		The transition direction. If 1, transition forward. If -1,
		//		transition backward. For example, the slide transition slides
		//		the view from right to left when dir == 1, and from left to
		//		right when dir == -1.
		transitionDir: 1,

		// transitionOptions: Object
		//		A hash object that holds transition options.
		transitionOptions: null,

		// callback: Function|String
		//		A callback function that is called when the transition has been
		//		finished. A function reference, or name of a function in
		//		context.
		callback: null,

		// sync: Boolean
		//		If true, XHR for the view content specified with the url
		//		parameter is performed synchronously. If false, it is done
		//		asynchronously and the progress indicator is displayed while
		//		loading the content. This parameter is effective only when the
		//		url parameter is used.
		sync: true,

		// label: String
		//		A label of the item. If the label is not specified, innerHTML is
		//		used as a label.
		label: "",

		// toggle: Boolean
		//		If true, the item acts like a toggle button.
		toggle: false,

		// _duration: Number
		//		Duration of selection, milliseconds.
		_duration: 800,

		inheritParams: function(){
			var parent = this.getParent();
			if(parent){
				if(!this.transition){ this.transition = parent.transition; }
				if(this.icon && parent.iconBase &&
					parent.iconBase.charAt(parent.iconBase.length - 1) === '/'){
					this.icon = parent.iconBase + this.icon;
				}
				if(!this.icon){ this.icon = parent.iconBase; }
				if(!this.iconPos){ this.iconPos = parent.iconPos; }
			}
		},

		select: function(){
			// summary:
			//		Makes this widget in the selected state.
			// description:
			//		Subclass must implement.
		},

		deselect: function(){
			// summary:
			//		Makes this widget in the deselected state.
			// description:
			//		Subclass must implement.
		},

		defaultClickAction: function(e){
			if(this.toggle){
				if(this.selected){
					this.deselect();
				}else{
					this.select();
				}
			}else if(!this.selected){
				this.select();
				if(!this.selectOne){
					var _this = this;
					setTimeout(function(){
						_this.deselect();
					}, this._duration);
				}
				var transOpts;
				if(this.moveTo || this.href || this.url || this.scene){
					transOpts = {moveTo: this.moveTo, href: this.href, url: this.url, scene: this.scene, transition: this.transition, transitionDir: this.transitionDir};
				}else if(this.transitionOptions){
					transOpts = this.transitionOptions;
				}
				if(transOpts){
					return new TransitionEvent(this.domNode,transOpts,e).dispatch();
				}
			}
		},

		getParent: function(){
			// summary:
			//		Gets the parent widget.
			// description:
			//		Almost equivalent to _Contained#getParent, but this method
			//		does not cause a script error even if this widget has no
			//		parent yet.
			var ref = this.srcNodeRef || this.domNode;
			return ref && ref.parentNode ? registry.getEnclosingWidget(ref.parentNode) : null;
		},

		setTransitionPos: function(e){
			// summary:
			//		Stores the clicked position for later use.
			// description:
			//		Some of the transition animations (e.g. ScaleIn) needs the
			//		clicked position.
			var w = this;
			while(true){
				w = w.getParent();
				if(!w || w instanceof View){ break; }
			}
			if(w){
				w.clickedPosX = e.clientX;
				w.clickedPosY = e.clientY;
			}
		},

		transitionTo: function(moveTo, href, url, scene){
			// summary:
			//		Performs a view transition.
			// description:
			//		Given a transition destination, this method performs a view
			//		transition. This method is typically called when this item
			//		is clicked.
			if(config.isDebug){
				var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
					caller = (arguments.callee.caller || "unknown caller").toString();
				if(!alreadyCalledHash[caller]){
					kernel.deprecated(this.declaredClass + "::transitionTo() is deprecated." +
					caller, "", "2.0");
					alreadyCalledHash[caller] = true;
				}
			}
			new TransitionEvent(this.domNode, {moveTo: moveTo, href: href, url: url, scene: scene,
						transition: this.transition, transitionDir: this.transitionDir}).dispatch();
		}
	});
});
<commit_msg>Refs #14604 !strict. Use touchstart/touchmove/touchend instead of onclick to handle user's select operation. Some widget may want to invoke the action on touchstart. Some others may want to invoke it on touchend. This module supports such various cases.<commit_after>define([
	"dojo/_base/array",
	"dojo/_base/declare",
	"dojo/_base/window",
	"dojo/dom-class",
	"dijit/registry",
	"dijit/_Contained",
	"dijit/_WidgetBase",
	"./TransitionEvent",
	"./iconUtils",
	"./sniff"
], function(array, declare, win, domClass, registry, Contained, WidgetBase, TransitionEvent, iconUtils, has){

/*=====
	var Contained = dijit._Contained;
	var WidgetBase = dijit._WidgetBase;
	var TransitionEvent = dojox.mobile.TransitionEvent;
=====*/

	// module:
	//		dojox/mobile/_ItemBase
	// summary:
	//		A base class for item classes (e.g. ListItem, IconItem, etc.)

	return declare("dojox.mobile._ItemBase", [WidgetBase, Contained],{
		// summary:
		//		A base class for item classes (e.g. ListItem, IconItem, etc.)
		// description:
		//		_ItemBase is a base class for widgets that have capability to
		//		make a view transition when clicked.

		// icon: String
		//		An icon image to display. The value can be either a path for an
		//		image file or a class name of a DOM button. If icon is not
		//		specified, the iconBase parameter of the parent widget is used.
		icon: "",

		// iconPos: String
		//		The position of an aggregated icon. IconPos is comma separated
		//		values like top,left,width,height (ex. "0,0,29,29"). If iconPos
		//		is not specified, the iconPos parameter of the parent widget is
		//		used.
		iconPos: "", // top,left,width,height (ex. "0,0,29,29")

		// alt: String
		//		An alt text for the icon image.
		alt: "",

		// href: String
		//		A URL of another web page to go to.
		href: "",

		// hrefTarget: String
		//		A target that specifies where to open a page specified by
		//		href. The value will be passed to the 2nd argument of
		//		window.open().
		hrefTarget: "",

		// moveTo: String
		//		The id of the transition destination view which resides in the
		//		current page.
		//
		//		If the value has a hash sign ('#') before the id (e.g. #view1)
		//		and the dojo.hash module is loaded by the user application, the
		//		view transition updates the hash in the browser URL so that the
		//		user can bookmark the destination view. In this case, the user
		//		can also use the browser's back/forward button to navigate
		//		through the views in the browser history.
		//
		//		If null, transitions to a blank view.
		//		If '#', returns immediately without transition.
		moveTo: "",

		// scene: String
		//		The name of a scene. Used from dojox.mobile.app.
		scene: "",

		// clickable: Boolean
		//		If true, this item becomes clickable even if a transition
		//		destination (moveTo, etc.) is not specified.
		clickable: false,

		// url: String
		//		A URL of an html fragment page or JSON data that represents a
		//		new view content. The view content is loaded with XHR and
		//		inserted in the current page. Then a view transition occurs to
		//		the newly created view. The view is cached so that subsequent
		//		requests would not load the content again.
		url: "",

		// urlTarget: String
		//		Node id under which a new view will be created according to the
		//		url parameter. If not specified, The new view will be created as
		//		a sibling of the current view.
		urlTarget: "",

		// transition: String
		//		A type of animated transition effect. You can choose from the
		//		standard transition types, "slide", "fade", "flip", or from the
		//		extended transition types, "cover", "coverv", "dissolve",
		//		"reveal", "revealv", "scaleIn", "scaleOut", "slidev",
		//		"swirl", "zoomIn", "zoomOut". If "none" is specified, transition
		//		occurs immediately without animation.
		transition: "",

		// transitionDir: Number
		//		The transition direction. If 1, transition forward. If -1,
		//		transition backward. For example, the slide transition slides
		//		the view from right to left when dir == 1, and from left to
		//		right when dir == -1.
		transitionDir: 1,

		// transitionOptions: Object
		//		A hash object that holds transition options.
		transitionOptions: null,

		// callback: Function|String
		//		A callback function that is called when the transition has been
		//		finished. A function reference, or name of a function in
		//		context.
		callback: null,

		// label: String
		//		A label of the item. If the label is not specified, innerHTML is
		//		used as a label.
		label: "",

		// toggle: Boolean
		//		If true, the item acts like a toggle button.
		toggle: false,

		// selected: Boolean
		//		If true, the item is highlighted to indicate it is selected.
		selected: false,

		// paramsToInherit: String
		//		Comma separated parameters to inherit from the parent.
		paramsToInherit: "transition,icon",

		// _selStartMethod: String
		//		Specifies how the item enters the selected state.
		//		"touch": Use touch events to enter the selected state.
		//		"none": Do not change the selected state.
		_selStartMethod: "none", // touch or none

		// _selEndMethod: String
		//		Specifies how the item leaves the selected state.
		//		"touch": Use touch events to leave the selected state.
		//		"timer": Use setTimeout to leave the selected state.
		//		"none": Do not change the selected state.
		_selEndMethod: "none", // touch, timer, or none

		// _delayedSelection: Boolean
		//		If true, selection is delayed 100ms and canceled if dragged in
		//		order to avoid selection when flick operation is performed.
		_delayedSelection: false,

		// _duration: Number
		//		Duration of selection, milliseconds.
		_duration: 800,

		// _handleClick: Boolean
		//		If true, this widget listens to touch events.
		_handleClick: true,

		buildRendering: function(){
			this.inherited(arguments);
			this._isOnLine = this.inheritParams();
		},

		startup: function(){
			if(this._started){ return; }
			if(!this._isOnLine){
				this.inheritParams();
			}
			if(this._handleClick && this._selStartMethod === "touch"){
				this._onTouchStartHandle = this.connect(this.domNode, has('touch') ? "ontouchstart" : "onmousedown", "_onTouchStart");
			}
			this.inherited(arguments);
		},

		inheritParams: function(){
			var parent = this.getParent();
			if(parent){
				array.forEach(this.paramsToInherit.split(/,/), function(p){
					if(p.match(/icon/i)){
						var base = p + "Base", pos = p + "Pos";
						if(this[p] && parent[base] &&
							parent[base].charAt(parent[base].length - 1) === '/'){
							this[p] = parent[base] + this[p];
						}
						if(!this[p]){ this[p] = parent[base]; }
						if(!this[pos]){ this[pos] = parent[pos]; }
					}else{
						if(!this[p]){ this[p] = parent[p]; }
					}
				}, this);
			}
			return !!parent;
		},

		userClickAction: function(e){
			// summary:
			//		User defined click action
		},

		defaultClickAction: function(e){
			// summary:
			//		The default action of this item
			this.handleSelection(e);
			if(this.userClickAction(e) === false){ return; } // user's click action
			this.makeTransition(e);
		},

		handleSelection: function(e){
			// summary:
			//		Handles this items selection state
			if(this._onTouchEndHandle){
				this.disconnect(this._onTouchEndHandle);
				this._onTouchEndHandle = null;
			}

			var p = this.getParent();
			if(this.toggle){
				this.set("selected", !this._currentSel);
			}else if(p && p.selectOne){
				this.set("selected", true);
			}else{
				if(this._selEndMethod === "touch"){
					this.set("selected", false);
				}else if(this._selEndMethod === "timer"){
					var _this = this;
					setTimeout(function(){
						_this.set("selected", false);
					}, this._duration);
				}
			}
		},

		makeTransition: function(e){
			if (this.href && this.hrefTarget) {
				win.global.open(this.href, this.hrefTarget || "_blank");
				this._onNewWindowOpened(e);
				return;
			}
			var transOpts = this.transitionOptions || {};
			var doTransition = false;
			if(this.moveTo || this.href || this.url || this.scene){
				array.forEach(["moveTo", "href", "hrefTarget", "url",
					"urlTarget", "scene", "transition", "transitionDir"], function(p){
					if(this[p]){
						transOpts[p] = this[p];
					}
				}, this);
				doTransition = true;
			}
			if(this._prepareForTransition(e, doTransition ? transOpts : null) === false){ return; }
			if(doTransition){
				this.setTransitionPos(e);
				new TransitionEvent(this.domNode, transOpts, e).dispatch();
			}
		},

		_onNewWindowOpened: function(e){
			// subclass may want to implement
		},

		_prepareForTransition: function(e, /*Object*/ transOpts){
			// subclass may want to implement
		},

		_onTouchStart: function(e){
			if(!this._onTouchEndHandle && this._selStartMethod === "touch"){
				// Connect to the entire window. Otherwise, fail to receive
				// events if operation is performed outside this widget.
				// Expose both connect handlers in case the user has interest.
				this._onTouchMoveHandle = this.connect(win.body(), has('touch') ? "ontouchmove" : "onmousemove", "_onTouchMove");
				this._onTouchEndHandle = this.connect(win.body(), has('touch') ? "ontouchend" : "onmouseup", "_onTouchEnd");
			}
			this.touchStartY = e.touches ? e.touches[0].pageY : e.clientY;
			this._currentSel = this.selected;

			if(this._delayedSelection){
				// so as not to make selection when the user flicks on ScrollableView
				this._selTimer = setTimeout(dojo.hitch(this, function(){ this.set("selected", true); }), 100);
			}else{
				this.set("selected", true);
			}
		},

		_onTouchMove: function(e){
			var y = e.touches ? e.touches[0].pageY : e.clientY;
			if(Math.abs(y - this.touchStartY) >= 4){ // dojox.mobile.scrollable#threshold
				if(this._selTimer){
					clearTimeout(this._selTimer);
					this._selTimer = null;
				}
				this._disconnect();
				this.set("selected", false);
			}
		},

		_disconnect: function(){
			this.disconnect(this._onTouchMoveHandle);
			this.disconnect(this._onTouchEndHandle);
			this._onTouchMoveHandle = this._onTouchEndHandle = null;
		},

		_onTouchEnd: function(e){
			this._disconnect();
			if(this._selTimer){
				clearTimeout(this._selTimer);
				this._selTimer = null;
			}else if (this._delayedSelection){
				return;
			}
			this._onClick(e);
		},

		setTransitionPos: function(e){
			// summary:
			//		Stores the clicked position for later use.
			// description:
			//		Some of the transition animations (e.g. ScaleIn) needs the
			//		clicked position.
			var w = this;
			while(true){
				w = w.getParent();
				if(!w || domClass.contains(w.domNode, "mblView")){ break; }
			}
			if(w){
				w.clickedPosX = e.clientX;
				w.clickedPosY = e.clientY;
			}
		},

		transitionTo: function(/*String|Object*/moveTo, href, url, scene){
			// summary:
			//		Performs a view transition.
			// description:
			//		Given a transition destination, this method performs a view
			//		transition. This method is typically called when this item
			//		is clicked.
			var opts = (typeof(moveTo) === "object") ? moveTo :
				{moveTo: moveTo, href: href, url: url, scene: scene,
				 transition: this.transition, transitionDir: this.transitionDir};
			new TransitionEvent(this.domNode, opts).dispatch();
		},

		_setIconAttr: function(icon){
			if(!this._isOnLine){ return; } // icon may be invalid because inheritParams is not called yet
			this._set("icon", icon);
			this.iconNode = iconUtils.setIcon(icon, this.iconPos, this.iconNode, this.alt, this.iconParentNode);
		},

		_setLabelAttr: function(/*String*/text){
			this._set("label", text);
			this.labelNode.innerHTML = this._cv ? this._cv(text) : text;
		},

		_setSelectedAttr: function(/*Boolean*/selected){
			// summary:
			//		Makes this widget in the selected or unselected state.
			// description:
			//		Subclass should override.
			if(selected){
				var p = this.getParent();
				if(p && p.selectOne){
					// deselect the currently selected item
					var arr = array.filter(p.getChildren(), function(w){
						return w.selected;
					});
					array.forEach(arr, function(c){
						this._prevSel = c;
						c.set("selected", false);
					}, this);
				}
			}
			this._set("selected", selected);
		}
	});
});
<|endoftext|>


<commit_before>/**
 * Created by ssehacker on 2016/10/8.
 */
var serve = require('koa-static');
var koa = require('koa');
var path = require('path');
var apiRoute = require('./api');
var app = koa();

var router = require('koa-router')();

app.use(serve(path.resolve(__dirname , '../dist')));
app.use(serve(path.resolve(__dirname ,'../public')));

var views = require('koa-views');

console.log(path.resolve(__dirname, '../public'));
app.use(views( path.resolve(__dirname, '../public') ));

//中间件： 打印访问路径,耗时
app.use(function *(next){
	var start = Date.now();

	yield next;
	var time = Date.now()-start;
	console.log(this.request.method +'\t'+this.request.url +'\t' + time + 'ms');
});

router.get('/', function * (next) {
	// let pathName = this.request.url;
	// pathName = pathName.substring(pathName.indexOf('/'));
	// pathName = pathName || 'demo';
	yield this.render('index');
});

router.get('/:module', function * (next) {
	// console.log(this.params.module);
	yield this.render('index');
});

app
	.use(apiRoute.routes())
	.use(router.routes())
	.use(router.allowedMethods());

app.listen(3005);

console.log('listening on port 3005');<commit_msg>fix port listen EADDRINUSE.<commit_after>/**
 * Created by ssehacker on 2016/10/8.
 */
var serve = require('koa-static');
var koa = require('koa');
var path = require('path');
var apiRoute = require('./api');
var app = koa();

var router = require('koa-router')();

app.use(serve(path.resolve(__dirname , '../dist')));
app.use(serve(path.resolve(__dirname ,'../public')));

var views = require('koa-views');

console.log(path.resolve(__dirname, '../public'));
app.use(views( path.resolve(__dirname, '../public') ));

//中间件： 打印访问路径,耗时
app.use(function *(next){
	var start = Date.now();

	yield next;
	var time = Date.now()-start;
	console.log(this.request.method +'\t'+this.request.url +'\t' + time + 'ms');
});

router.get('/', function * (next) {
	// let pathName = this.request.url;
	// pathName = pathName.substring(pathName.indexOf('/'));
	// pathName = pathName || 'demo';
	yield this.render('index');
});

router.get('/:module', function * (next) {
	// console.log(this.params.module);
	yield this.render('index');
});

app
	.use(apiRoute.routes())
	.use(router.routes())
	.use(router.allowedMethods());

app.listen(3006);

console.log('listening on port 3006');<|endoftext|>


<commit_before>

"use strict";
var Main = module.parent.exports;

Main.ActionHSET = function(Request){
  if(Request.length < 3){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    let HValue = Main.NormalizeType(Request[Number + 1]);
    Value.set(Request[Number], HValue);
  }
  return {Type: 'OK'};
};

Main.ActionHGET = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HGET', Value);

  if(Request.length === 1){
    return Value && Value.get(Request[0]) || '';
  } else {
    return Request.map(function(Key){
      return Value && Value.get(Key) || '';
    });
  }
};

Main.ActionHDEL = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Length = 0;
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HDEL', Value);
  else
    return Length;

  Request.forEach(function(Name){
    Value.delete(Name);
    Length ++;
  }.bind(this));
  return Length;
};

Main.ActionHEXPIRE = function(Request){
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  let ToReturn = {Type: 'OK'};

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXPIRE', Value);

  for(let Number = 0; Number < Request.length; Number += 2){
    let HKey = Request[Number];
    let HTimeout = Main.NormalizeType(Request[Number + 1]);
    if(typeof HTimeout !== 'number')
      throw new Error("EXPIRE Expects even parameters to be numeric");
    let TimeoutKey = Symbol.for(Key + "\n" + HKey);
    clearTimeout(this.Timeouts[TimeoutKey]);
    this.Timeouts[TimeoutKey] = setTimeout(function(HKey){
      Main.ActionHDEL.call(this, [Key, HKey]);
    }.bind(this, HKey), HTimeout * 1000);
  }

  return ToReturn;
};

Main.ActionHEXISTS = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);

  if(Request.length === 1){
    return (Value && Value.has(Request[0])) === true ? 1 : 0;
  } else {
    return Request.map(function(HKey){
      return (Value && Value.has(HKey)) === true ? 1 : 0;
    });
  }
};

Main.ActionHGETALL = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  Value.forEach(function(Value, Key){
    ToReturn.push(Key);
    ToReturn.push(Value);
  });
  return ToReturn;
};

Main.ActionHKEYS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.keys();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHLEN = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return 0;
  return Value.size
};

Main.ActionHVALS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.values();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHMGET = Main.ActionHGET;
Main.ActionHMSET = Main.ActionHSET;

// Custom Functions
/**
 * I personally needed the features below to get this kind-of behaviour
 * =======================
 * QP:USERS {
 *  USER:1 {
 *   NAME: "Steel"
 *   USERNAME: "SteelBrain"
 *  }
 * }
 */

/**
 * HHSET KEY HKEY HHKEY HHVALUE HHKEY2 HHVALUE2
 */
Main.ActionHHSET = function(Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);
  if(Request.length < 4){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HHSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  let HKey = Request.shift();
  let HValue = Value.get(HKey);
  try {
    Main.Validate(Main.VAL_HASH, 'HHSET', HValue);
  } catch(err){
    HValue = new Map();
    Value.set(HKey, HValue);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    HValue.set(Request[Number], Main.NormalizeType(Request[Number + 1]));
  }
  return {Type: 'OK'};
};

/**
 * HHGET KEY HKEY HHKEY1 HHKEY2
 */
Main.ActionHHGET = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let DefaultValue = Request.length === 2 ? '' : [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHGET', Value);
  else return DefaultValue;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHGET', HValue);
  if(Request.length === 1){
    return HValue.get(Request[0]) || '';
  } else {
    return Request.map((Name) => HValue.get(Name) || '')
  }
};

/**
 * HHDEL KEY HKEY HHKEY1 HHKEY2
 */
Main.ActionHHDEL = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = 0;

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHDEL', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHDEL', HValue);
  Request.forEach((Name) => HValue.delete(Name) && ++ToReturn);
  return ToReturn
};

/**
 * HHGETALL KEY HKEY
 */
Main.ActionHHGETALL = function(Request){
  Main.ValidateArguments(2, Request.length);

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HGETALL', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HGETALL', HValue);
  HValue.forEach(function(Value, Key){
    ToReturn.push(Key);
    ToReturn.push(Value);
  });
  return ToReturn;
};

/**
 * HHEXISTS KEY HKEY HHKEY HHKEY2...
 */
Main.ActionHHEXISTS = function(Request){
  if(Request.length < 3){
    throw new Error(Main.MSG_ARGS_LESS);
  }

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = Request.length === 2 ? '' : [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHEXISTS', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHEXISTS', HValue);

  if(Request.length === 1){
    return HValue.has(Request[0]) ? 1 : 0;
  } else {
    return Request.map((Name) => HValue.has(Name) ? 1 : 0);
  }
};

/**
 * HHEXPIRE KEY HKEY HHKEY1 HHKEY1Timeout HHKEY2 HHKEY2Timeout...
 */
Main.ActionHHEXPIRE = function(Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = {Type: "OK"};

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHEXISTS', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHEXISTS', HValue);

  for(let Number = 0; Number < Request.length; Number += 2){
    let HHKey = Request[Number];
    let HHTimeout = Main.NormalizeType(Request[Number + 1]);
    if(typeof HHTimeout !== 'number')
      throw new Error("EXPIRE Expects even parameters to be numeric");
    let TimeoutKey = Symbol.for(Key + "\n" + HKey + "\n" + HHKey);
    clearTimeout(this.Timeouts[TimeoutKey]);
    this.Timeouts[TimeoutKey] = setTimeout(function(HKey, HHKey){
      Main.ActionHHDEL.call(this, [Key, HKey, HHKey]);
    }.bind(this, HKey, HHKey), HHTimeout * 1000);
  }

  return ToReturn;
};<commit_msg>:new: Use Custom Map in Methods_Hash<commit_after>

"use strict";
var
  Main = module.parent.exports,
  Map = require('a-map');

Main.ActionHSET = function(Request){
  if(Request.length < 3){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    let HValue = Main.NormalizeType(Request[Number + 1]);
    Value.set(Request[Number], HValue);
  }
  return {Type: 'OK'};
};

Main.ActionHGET = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HGET', Value);

  if(Request.length === 1){
    return Value && Value.get(Request[0]) || '';
  } else {
    return Request.map(function(Key){
      return Value && Value.get(Key) || '';
    });
  }
};

Main.ActionHDEL = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Length = 0;
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HDEL', Value);
  else
    return Length;

  Request.forEach(function(Name){
    Value.delete(Name);
    Length ++;
  }.bind(this));
  return Length;
};

Main.ActionHEXPIRE = function(Request){
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  let ToReturn = {Type: 'OK'};

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXPIRE', Value);

  for(let Number = 0; Number < Request.length; Number += 2){
    let HKey = Request[Number];
    let HTimeout = Main.NormalizeType(Request[Number + 1]);
    if(typeof HTimeout !== 'number')
      throw new Error("EXPIRE Expects even parameters to be numeric");
    let TimeoutKey = Symbol.for(Key + "\n" + HKey);
    clearTimeout(this.Timeouts[TimeoutKey]);
    this.Timeouts[TimeoutKey] = setTimeout(function(HKey){
      Main.ActionHDEL.call(this, [Key, HKey]);
    }.bind(this, HKey), HTimeout * 1000);
  }

  return ToReturn;
};

Main.ActionHEXISTS = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);

  if(Request.length === 1){
    return (Value && Value.has(Request[0])) === true ? 1 : 0;
  } else {
    return Request.map(function(HKey){
      return (Value && Value.has(HKey)) === true ? 1 : 0;
    });
  }
};

Main.ActionHGETALL = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  Value.forEach(function(Value, Key){
    ToReturn.push(Key);
    ToReturn.push(Value);
  });
  return ToReturn;
};

Main.ActionHKEYS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.keys();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHLEN = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return 0;
  return Value.size
};

Main.ActionHVALS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.values();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHMGET = Main.ActionHGET;
Main.ActionHMSET = Main.ActionHSET;

// Custom Functions
/**
 * I personally needed the features below to get this kind-of behaviour
 * =======================
 * QP:USERS {
 *  USER:1 {
 *   NAME: "Steel"
 *   USERNAME: "SteelBrain"
 *  }
 * }
 */

/**
 * HHSET KEY HKEY HHKEY HHVALUE HHKEY2 HHVALUE2
 */
Main.ActionHHSET = function(Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);
  if(Request.length < 4){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HHSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  let HKey = Request.shift();
  let HValue = Value.get(HKey);
  try {
    Main.Validate(Main.VAL_HASH, 'HHSET', HValue);
  } catch(err){
    HValue = new Map();
    Value.set(HKey, HValue);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    HValue.set(Request[Number], Main.NormalizeType(Request[Number + 1]));
  }
  return {Type: 'OK'};
};

/**
 * HHGET KEY HKEY HHKEY1 HHKEY2
 */
Main.ActionHHGET = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let DefaultValue = Request.length === 2 ? '' : [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHGET', Value);
  else return DefaultValue;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHGET', HValue);
  if(Request.length === 1){
    return HValue.get(Request[0]) || '';
  } else {
    return Request.map((Name) => HValue.get(Name) || '')
  }
};

/**
 * HHDEL KEY HKEY HHKEY1 HHKEY2
 */
Main.ActionHHDEL = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = 0;

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHDEL', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHDEL', HValue);
  Request.forEach((Name) => HValue.delete(Name) && ++ToReturn);
  return ToReturn
};

/**
 * HHGETALL KEY HKEY
 */
Main.ActionHHGETALL = function(Request){
  Main.ValidateArguments(2, Request.length);

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HGETALL', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HGETALL', HValue);
  HValue.forEach(function(Value, Key){
    ToReturn.push(Key);
    ToReturn.push(Value);
  });
  return ToReturn;
};

/**
 * HHEXISTS KEY HKEY HHKEY HHKEY2...
 */
Main.ActionHHEXISTS = function(Request){
  if(Request.length < 3){
    throw new Error(Main.MSG_ARGS_LESS);
  }

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = Request.length === 2 ? '' : [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHEXISTS', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHEXISTS', HValue);

  if(Request.length === 1){
    return HValue.has(Request[0]) ? 1 : 0;
  } else {
    return Request.map((Name) => HValue.has(Name) ? 1 : 0);
  }
};

/**
 * HHEXPIRE KEY HKEY HHKEY1 HHKEY1Timeout HHKEY2 HHKEY2Timeout...
 */
Main.ActionHHEXPIRE = function(Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  let ToReturn = {Type: "OK"};

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HHEXISTS', Value);
  else return ToReturn;

  let HKey = Request.shift();
  let HValue = Value.get(HKey);

  Main.Validate(Main.VAL_HASH, 'HHEXISTS', HValue);

  for(let Number = 0; Number < Request.length; Number += 2){
    let HHKey = Request[Number];
    let HHTimeout = Main.NormalizeType(Request[Number + 1]);
    if(typeof HHTimeout !== 'number')
      throw new Error("EXPIRE Expects even parameters to be numeric");
    let TimeoutKey = Symbol.for(Key + "\n" + HKey + "\n" + HHKey);
    clearTimeout(this.Timeouts[TimeoutKey]);
    this.Timeouts[TimeoutKey] = setTimeout(function(HKey, HHKey){
      Main.ActionHHDEL.call(this, [Key, HKey, HHKey]);
    }.bind(this, HKey, HHKey), HHTimeout * 1000);
  }

  return ToReturn;
};<|endoftext|>


<commit_before>/**
 * Sitespeed.io - How speedy is your site? (http://www.sitespeed.io)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
'use strict';
var util = require('../util/util'),
	winston = require('winston'),
	net = require('net');

function GraphiteCollector(config) {
	this.config = config;
	this.namespace = this.config.graphiteNamespace;
	this.log = winston.loggers.get('sitespeed.io');
	this.timeStamp = ' ' + Math.round(new Date().getTime() / 1000) + '\n';
}

GraphiteCollector.prototype.collect = function(aggregates, pages, domains) {

	var config = this.config;
	var self = this;
	var statistics = '';
	pages.forEach(function(page) {
		statistics += self._getPageStats(page);
	});

	if (this.config.graphiteData.indexOf('summary') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		statistics += this._getSummaryStats(aggregates, config.urlObject.hostname, pages.length);
	}

	statistics += self._getDomainStats(domains, config.urlObject.hostname);

	return statistics;

};

GraphiteCollector.prototype._getPageStats = function(page) {

	var statistics = '';

	var urlKey = util.getGraphiteURLKey(decodeURIComponent(page.url));

	// lets collect the specific data per
	statistics += this._getRuleStats(page, urlKey);
	statistics += this._getBrowserTimeStats(page, urlKey);
	statistics += this._getPageMetricsStats(page, urlKey);
	statistics += this._getGPSIStats(page, urlKey);
	statistics += this._getWPTStats(page, urlKey);
	statistics += this._getAssetsStats(page, urlKey);

	return statistics;

};

GraphiteCollector.prototype._getRuleStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	if (page.yslow && (this.config.graphiteData.indexOf('rules') > -1 || this.config.graphiteData.indexOf(
			'all') > -1)) {
		Object.keys(page.rules).forEach(function(rule) {
			statistics += self.namespace + '.' + urlKey + 'rules.' + rule + ' ' +
				page.rules[rule].v + self.timeStamp;
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getBrowserTimeStats = function(page, urlKey) {

	var statistics = '';
	var statsWeWillPush = ['min', 'median', 'p90', 'max'];
	var self = this;

	// the timings that are not browser specific
	if (this.config.graphiteData.indexOf('timings') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {

    // the types we have in our page object
		var types = ['timings', 'custom', 'extras'];

		types.forEach(function(type) {

      // check that we actually collect browser data
			if (page[type]) {
				Object.keys(page[type]).forEach(function(timing) {
					statsWeWillPush.forEach(function(val) {
						// is it a browser?
						if (self.config.supportedBrowsers.indexOf(timing) < 0) {
							statistics += self.namespace + '.' + urlKey + type + '.' + timing +
								'.' + val + ' ' + page[type][timing][val].v + self.timeStamp;
						}
					});
				});

				// and the browsers
				Object.keys(page[type]).forEach(function(browser) {
					if (self.config.supportedBrowsers.indexOf(browser) > -1) {
						Object.keys(page[type][browser]).forEach(function(timing) {
							statsWeWillPush.forEach(function(val) {
								statistics += self.namespace + '.' + urlKey + type + '.' +
									browser + '.' + timing + '.' + val + ' ' + page[type][
										browser
									][timing][val].v + self.timeStamp;
							});
						});
					}
				});
			}
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getPageMetricsStats = function(page, urlKey) {

	var statistics = '';
	var self = this;

	if (this.config.graphiteData.indexOf('pagemetrics') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		// and all the assets
		if (page.yslow) {
			Object.keys(page.yslow.assets).forEach(function(asset) {
				statistics += self.namespace + '.' + urlKey + 'assets.' + asset +
					' ' + page.yslow.assets[asset].v + self.timeStamp;
			});

			// and page specific
			statistics += self.namespace + '.' + urlKey + 'score' + ' ' + page.score +
				self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'noRequests' + ' ' + page.yslow
				.requests.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'requestsMissingExpire' +
				' ' + page.yslow.requestsMissingExpire.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey +
				'timeSinceLastModification' + ' ' + page.yslow.timeSinceLastModification
				.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'cacheTime' + ' ' + page.yslow
				.cacheTime.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'pageWeight' + ' ' + page.yslow
				.pageWeight.v + self.timeStamp;
		}
	}

	return statistics;
};

GraphiteCollector.prototype._getGPSIStats = function(page, urlKey) {
	// add gspi score
	if (page.gpsi) {
		return this.namespace + '.' + urlKey + 'gpsi' + ' ' + page.gpsi.gscore.v + this.timeStamp;
	} else {
		return '';
	}
};

GraphiteCollector.prototype._getWPTStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	// add wpt data
	if (page.wpt) {
		Object.keys(page.wpt).forEach(function(location) {
			Object.keys(page.wpt[location]).forEach(function(browser) {
				Object.keys(page.wpt[location][browser]).forEach(function(connectivity) {
					Object.keys(page.wpt[location][browser][connectivity]).forEach(function(view) {
						Object.keys(page.wpt[location][browser][connectivity][view]).forEach(function(metric) {
							statistics += self.namespace + '.' + urlKey + 'wpt.' + location + '.' +
								connectivity + '.' + browser + '.' + view + '.' + metric + '.median ' +
								page.wpt[location][browser][connectivity][view][metric].v +
								self.timeStamp;
						});
					});
				});
			});
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getAssetsStats = function(page, urlKey) {

	var stats = '';

	if (this.config.graphiteData.indexOf('requests') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		if (page.har) {

			var self = this;
			var timings = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive'];
			page.har.forEach(function(har) {

				har.log.entries.forEach(function(entry) {

					var url = entry.request.url;

					try {
						url = decodeURIComponent(entry.request.url);
					} catch (error) {
						self.log.info('Couldn\'t decode URI:' + entry.request.url);
					}

					var assetURL = util.getGraphiteURLKey(url, '_');

					// remove the last ., we need to rewrite the logic for the
					// keys
					if(assetURL.substr(-1) === '.') {
						assetURL = assetURL.slice(0, -1);
					}

					// TODO when we get the HAR from WPT we should include the browser, location
					// & connectivity in the key

					// get the timestamp from the HAR when the action happend
					var timeStamp = ' ' + Math.round(new Date(entry.startedDateTime).getTime() / 1000) + '\n';
					var total = 0;
					if (entry.timings) {
						timings.forEach(function(timing) {
							total += entry.timings[timing];
							stats += self.namespace + '.' + urlKey + 'requests.' + assetURL + '.timings.' + timing +
								' ' + entry.timings[timing] + timeStamp;
						});
						stats += self.namespace + '.' + urlKey + 'requests.' + assetURL + '.timings.total ' + total + ' ' +
							timeStamp;
					}
					// lets also add the size & type when we are here
					// we use the timestamp for the whole run to make sure
					// we only get one entry, this can and should be cleaned up later
					stats += self.namespace + '.' + urlKey + 'requests.' + assetURL + '.type.' +
					util.getContentType(entry.response.content.mimeType) + '.size ' + entry.response.content.size + ' ' + self.timeStamp;

				});

			});
		}
	}
	return stats;
};

GraphiteCollector.prototype._getDomainStats = function(domains, hostname) {

	var stats = '';
	if (domains) {

		var self = this;
		var timings = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive', 'total'];
		var values = ['min', 'median', 'max'];

		domains.forEach(function(domain) {
			timings.forEach(function(timing) {
				values.forEach(function(value) {
					stats += self.namespace + '.summary.' + hostname + '.domains.timings.' + domain.domain.split('.').join('_') +
						'.' +
						timing + '.' +
						value + ' ' + util.getStatisticsObject(domain[timing].stats, 0)[value] + self.timeStamp;
				});
			});
			// and total time spent downloading
			stats += self.namespace + '.summary.' + hostname + '.domains.accumulatedTime.' + domain.domain.split('.').join('_') + ' ' + domain.accumulatedTime + self.timeStamp;

			// the number of requests
			stats += self.namespace + '.summary.' + hostname + '.domains.requests.' + domain.domain.split('.').join('_') + '' +
				' ' + domain
				.count +
				self.timeStamp;
			// and the size, we only have the size for requests in the first HAR right now
			if (domain.size) {
				Object.keys(domain.size).forEach(function(size) {
					stats += self.namespace + '.summary.' + hostname + '.domains.size.' + domain.domain.split('.').join('_') + '.' + size +
					' ' + domain.size[size] +
					self.timeStamp;
				});
			}
		});
	}

	return stats;
};

GraphiteCollector.prototype._getSummaryStats = function(aggregates, hostname, noOfPages) {
	var statistics = '';
	var self = this;
	var values = ['min', 'p10', 'median', 'mean', 'p90', 'p99', 'max'];

	aggregates.forEach(function(aggregate) {
		values.forEach(function(value) {
			// special handling for WPT values for now
			if (aggregate.id.indexOf('WPT') > -1) {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.wpt.' +
					aggregate.key + '.' + value + ' ' + aggregate.stats[value] + self.timeStamp;

			} else {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.' + aggregate.id +
					'.' + value + ' ' + aggregate.stats[value] + self.timeStamp;
			}
		});
	});

	// and add the number of runs
	statistics += self.namespace + '.summary.' + hostname + '.runsPerBrowser ' + this.config.no + this.timeStamp;

	// and number of tested pages per
	statistics += this.namespace + '.summary.' + hostname + '.testedPages ' + noOfPages + this.timeStamp;

	return statistics;
};

module.exports = GraphiteCollector;
<commit_msg>super ugly fix for dubplicate dots in graphite keys #654<commit_after>/**
 * Sitespeed.io - How speedy is your site? (http://www.sitespeed.io)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
'use strict';
var util = require('../util/util'),
	winston = require('winston'),
	net = require('net');

function GraphiteCollector(config) {
	this.config = config;
	this.namespace = this.config.graphiteNamespace;
	this.log = winston.loggers.get('sitespeed.io');
	this.timeStamp = ' ' + Math.round(new Date().getTime() / 1000) + '\n';
}

GraphiteCollector.prototype.collect = function(aggregates, pages, domains) {

	var config = this.config;
	var self = this;
	var statistics = '';
	pages.forEach(function(page) {
		statistics += self._getPageStats(page);
	});

	if (this.config.graphiteData.indexOf('summary') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		statistics += this._getSummaryStats(aggregates, config.urlObject.hostname, pages.length);
	}

	statistics += self._getDomainStats(domains, config.urlObject.hostname);

	// FIXME We have a problem with double dots and we need to fix the original
	// issue on 4.0.2
	statistics = statistics.split('..').join('.');

	return statistics;

};

GraphiteCollector.prototype._getPageStats = function(page) {

	var statistics = '';

	var urlKey = util.getGraphiteURLKey(decodeURIComponent(page.url));

	// lets collect the specific data per
	statistics += this._getRuleStats(page, urlKey);
	statistics += this._getBrowserTimeStats(page, urlKey);
	statistics += this._getPageMetricsStats(page, urlKey);
	statistics += this._getGPSIStats(page, urlKey);
	statistics += this._getWPTStats(page, urlKey);
	statistics += this._getAssetsStats(page, urlKey);

	return statistics;

};

GraphiteCollector.prototype._getRuleStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	if (page.yslow && (this.config.graphiteData.indexOf('rules') > -1 || this.config.graphiteData.indexOf(
			'all') > -1)) {
		Object.keys(page.rules).forEach(function(rule) {
			statistics += self.namespace + '.' + urlKey + 'rules.' + rule + ' ' +
				page.rules[rule].v + self.timeStamp;
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getBrowserTimeStats = function(page, urlKey) {

	var statistics = '';
	var statsWeWillPush = ['min', 'median', 'p90', 'max'];
	var self = this;

	// the timings that are not browser specific
	if (this.config.graphiteData.indexOf('timings') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {

    // the types we have in our page object
		var types = ['timings', 'custom', 'extras'];

		types.forEach(function(type) {

      // check that we actually collect browser data
			if (page[type]) {
				Object.keys(page[type]).forEach(function(timing) {
					statsWeWillPush.forEach(function(val) {
						// is it a browser?
						if (self.config.supportedBrowsers.indexOf(timing) < 0) {
							statistics += self.namespace + '.' + urlKey + type + '.' + timing +
								'.' + val + ' ' + page[type][timing][val].v + self.timeStamp;
						}
					});
				});

				// and the browsers
				Object.keys(page[type]).forEach(function(browser) {
					if (self.config.supportedBrowsers.indexOf(browser) > -1) {
						Object.keys(page[type][browser]).forEach(function(timing) {
							statsWeWillPush.forEach(function(val) {
								statistics += self.namespace + '.' + urlKey + type + '.' +
									browser + '.' + timing + '.' + val + ' ' + page[type][
										browser
									][timing][val].v + self.timeStamp;
							});
						});
					}
				});
			}
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getPageMetricsStats = function(page, urlKey) {

	var statistics = '';
	var self = this;

	if (this.config.graphiteData.indexOf('pagemetrics') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		// and all the assets
		if (page.yslow) {
			Object.keys(page.yslow.assets).forEach(function(asset) {
				statistics += self.namespace + '.' + urlKey + 'assets.' + asset +
					' ' + page.yslow.assets[asset].v + self.timeStamp;
			});

			// and page specific
			statistics += self.namespace + '.' + urlKey + 'score' + ' ' + page.score +
				self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'noRequests' + ' ' + page.yslow
				.requests.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'requestsMissingExpire' +
				' ' + page.yslow.requestsMissingExpire.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey +
				'timeSinceLastModification' + ' ' + page.yslow.timeSinceLastModification
				.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'cacheTime' + ' ' + page.yslow
				.cacheTime.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'pageWeight' + ' ' + page.yslow
				.pageWeight.v + self.timeStamp;
		}
	}

	return statistics;
};

GraphiteCollector.prototype._getGPSIStats = function(page, urlKey) {
	// add gspi score
	if (page.gpsi) {
		return this.namespace + '.' + urlKey + 'gpsi' + ' ' + page.gpsi.gscore.v + this.timeStamp;
	} else {
		return '';
	}
};

GraphiteCollector.prototype._getWPTStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	// add wpt data
	if (page.wpt) {
		Object.keys(page.wpt).forEach(function(location) {
			Object.keys(page.wpt[location]).forEach(function(browser) {
				Object.keys(page.wpt[location][browser]).forEach(function(connectivity) {
					Object.keys(page.wpt[location][browser][connectivity]).forEach(function(view) {
						Object.keys(page.wpt[location][browser][connectivity][view]).forEach(function(metric) {
							statistics += self.namespace + '.' + urlKey + 'wpt.' + location + '.' +
								connectivity + '.' + browser + '.' + view + '.' + metric + '.median ' +
								page.wpt[location][browser][connectivity][view][metric].v +
								self.timeStamp;
						});
					});
				});
			});
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getAssetsStats = function(page, urlKey) {

	var stats = '';

	if (this.config.graphiteData.indexOf('requests') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		if (page.har) {

			var self = this;
			var timings = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive'];
			page.har.forEach(function(har) {

				har.log.entries.forEach(function(entry) {

					var url = entry.request.url;

					try {
						url = decodeURIComponent(entry.request.url);
					} catch (error) {
						self.log.info('Couldn\'t decode URI:' + entry.request.url);
					}

					var assetURL = util.getGraphiteURLKey(url, '_');

					// remove the last ., we need to rewrite the logic for the
					// keys
					if(assetURL.substr(-1) === '.') {
						assetURL = assetURL.slice(0, -1);
					}

					// TODO when we get the HAR from WPT we should include the browser, location
					// & connectivity in the key

					// get the timestamp from the HAR when the action happend
					var timeStamp = ' ' + Math.round(new Date(entry.startedDateTime).getTime() / 1000) + '\n';
					var total = 0;
					if (entry.timings) {
						timings.forEach(function(timing) {
							total += entry.timings[timing];
							stats += self.namespace + '.' + urlKey + 'requests.' + assetURL + '.timings.' + timing +
								' ' + entry.timings[timing] + timeStamp;
						});
						stats += self.namespace + '.' + urlKey + 'requests.' + assetURL + '.timings.total ' + total + ' ' +
							timeStamp;
					}
					// lets also add the size & type when we are here
					// we use the timestamp for the whole run to make sure
					// we only get one entry, this can and should be cleaned up later
					stats += self.namespace + '.' + urlKey + 'requests.' + assetURL + '.type.' +
					util.getContentType(entry.response.content.mimeType) + '.size ' + entry.response.content.size + ' ' + self.timeStamp;

				});

			});
		}
	}
	return stats;
};

GraphiteCollector.prototype._getDomainStats = function(domains, hostname) {

	var stats = '';
	if (domains) {

		var self = this;
		var timings = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive', 'total'];
		var values = ['min', 'median', 'max'];

		domains.forEach(function(domain) {
			timings.forEach(function(timing) {
				values.forEach(function(value) {
					stats += self.namespace + '.summary.' + hostname + '.domains.timings.' + domain.domain.split('.').join('_') +
						'.' +
						timing + '.' +
						value + ' ' + util.getStatisticsObject(domain[timing].stats, 0)[value] + self.timeStamp;
				});
			});
			// and total time spent downloading
			stats += self.namespace + '.summary.' + hostname + '.domains.accumulatedTime.' + domain.domain.split('.').join('_') + ' ' + domain.accumulatedTime + self.timeStamp;

			// the number of requests
			stats += self.namespace + '.summary.' + hostname + '.domains.requests.' + domain.domain.split('.').join('_') + '' +
				' ' + domain
				.count +
				self.timeStamp;
			// and the size, we only have the size for requests in the first HAR right now
			if (domain.size) {
				Object.keys(domain.size).forEach(function(size) {
					stats += self.namespace + '.summary.' + hostname + '.domains.size.' + domain.domain.split('.').join('_') + '.' + size +
					' ' + domain.size[size] +
					self.timeStamp;
				});
			}
		});
	}

	return stats;
};

GraphiteCollector.prototype._getSummaryStats = function(aggregates, hostname, noOfPages) {
	var statistics = '';
	var self = this;
	var values = ['min', 'p10', 'median', 'mean', 'p90', 'p99', 'max'];

	aggregates.forEach(function(aggregate) {
		values.forEach(function(value) {
			// special handling for WPT values for now
			if (aggregate.id.indexOf('WPT') > -1) {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.wpt.' +
					aggregate.key + '.' + value + ' ' + aggregate.stats[value] + self.timeStamp;

			} else {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.' + aggregate.id +
					'.' + value + ' ' + aggregate.stats[value] + self.timeStamp;
			}
		});
	});

	// and add the number of runs
	statistics += self.namespace + '.summary.' + hostname + '.runsPerBrowser ' + this.config.no + this.timeStamp;

	// and number of tested pages per
	statistics += this.namespace + '.summary.' + hostname + '.testedPages ' + noOfPages + this.timeStamp;

	return statistics;
};

module.exports = GraphiteCollector;
<|endoftext|>


<commit_before>/**
 * This module takes care of loading the BridgeSupport XML files for a given
 * framework, and parsing the data into the given framework object.
 */

var fs = require('fs')
  , sax = require('sax')
  , path = require('path')
  , core = require('./core')
  , types = require('./types')
  , join = path.join
  , basename = path.basename
  , exists = path.existsSync
  , DY_SUFFIX = '.dylib'
  , BS_SUFFIX = '.bridgesupport'

function bridgesupport (fw, _global) {

  var bridgeSupportDir = join(fw.basePath, 'Resources', 'BridgeSupport')
    , bridgeSupportXML = join(bridgeSupportDir, fw.name + BS_SUFFIX)
    , bridgeSupportDylib = join(bridgeSupportDir, fw.name + DY_SUFFIX)

  // If there's no BridgeSupport file, then just return the empty object...
  if (!exists(bridgeSupportXML)) {
    console.warn('No BridgeSupport files found for framework "%s" at: %s', fw.name, bridgeSupportXML);
    return;
  }

  if (exists(bridgeSupportDylib)) {
    fw.inline = core.dlopen(bridgeSupportDylib);
  }

  var contents = fs.readFileSync(bridgeSupportXML, 'utf8')
    , parser = sax.parser(true)
    , gotEnd = false
    , classes = []
    , constants = []
    , curName
    , curRtnType
    , curArgTypes
    , isInline;
  //console.error(contents);

  parser.onerror = function (e) { throw e; }
  parser.onopentag = function (node) {
    //console.error(node);
    switch (node.name) {
      case 'depends_on':
        bridgesupport.import(node.attributes.path);
        break;
      case 'class':
        break;
      case 'method':
        break;
      case 'arg':
        if (curName) {
          // class methods also have args, we only want functions though
          curArgTypes.push(getType(node));
        }
        break;
      case 'retval':
        if (curName) {
          // class methods also have retvals, we only want functions though
          curRtnType = getType(node);
        }
        break;
      case 'signatures':
        // ignore
        break;
      case 'string_constant':
        _global[node.attributes.name] = node.attributes.value;
        break;
      case 'enum':
        _global[node.attributes.name] = parseInt(node.attributes.value);
        break;
      case 'struct':
        break;
      case 'field':
        //console.error(node);
        break;
      case 'cftype':
        break;
      case 'constant':
        //constants.push(node);
        break;
      case 'function':
        curName = node.attributes.name;
        curRtnType = null;
        curArgTypes = [];
        isInline = node.attributes.inline == 'true';
        // TODO: is variadic? will require a 'function generator'
        break;
      case 'args':
        break;
      case 'opaque':
        break;
      case 'informal_protocol':
        break;
      case 'function_alias':
        break;
      default:
        throw new Error('unkown tag: '+ node.name);
        break;
    }
  };
  parser.onclosetag = function (node) {
    switch (node) {
      case 'function':
        // Binded functions will be lazy-loaded. We simply define a getter that
        // does the creation magic the first time, and replaces the getter with
        // the binded function
        (function (curName, curRtnType, curArgTypes, isInline) {
          if (isInline && !fw.inline) throw new Error('declared inline but could not find inline dylib!');
          _global.__defineGetter__(curName, function () {
            var f = core.Function(curName, curRtnType, curArgTypes, false, isInline ? fw.inline : fw.lib);
            delete _global[curName];
            return _global[curName] = f;
          });
        })(curName, curRtnType, curArgTypes, isInline);
        curName = null;
        break;
    }
  };
  parser.onend = function () {
    gotEnd = true;
  };

  // Parse the contents of the file. This should happen synchronously.
  parser.write(contents).close();

  if (!gotEnd) {
    throw new Error('could not parse BridgeSupport files synchronously');
  }

  /*
  constants.forEach(function (node) {
    var sym = lib.get(node.attributes.name);
    if (node.attributes.declared_type == 'NSString*') {
      sym = Class.wrapId(sym);
      sym.__proto__ = Class.getClass('NSString').prototype;
      framework[node.attributes.name] = sym;
    }
  });
  */
}
module.exports = bridgesupport;

// TODO: Add 64-bit retrievial, 'type64'
function getType (node) {
  return types.map(node.attributes.type);
}
<commit_msg>Remove console.error debug comment.<commit_after>/**
 * This module takes care of loading the BridgeSupport XML files for a given
 * framework, and parsing the data into the given framework object.
 */

var fs = require('fs')
  , sax = require('sax')
  , path = require('path')
  , core = require('./core')
  , types = require('./types')
  , join = path.join
  , basename = path.basename
  , exists = path.existsSync
  , DY_SUFFIX = '.dylib'
  , BS_SUFFIX = '.bridgesupport'

function bridgesupport (fw, _global) {

  var bridgeSupportDir = join(fw.basePath, 'Resources', 'BridgeSupport')
    , bridgeSupportXML = join(bridgeSupportDir, fw.name + BS_SUFFIX)
    , bridgeSupportDylib = join(bridgeSupportDir, fw.name + DY_SUFFIX)

  // If there's no BridgeSupport file, then just return the empty object...
  if (!exists(bridgeSupportXML)) {
    //console.warn('No BridgeSupport files found for framework "%s" at: %s', fw.name, bridgeSupportXML);
    return;
  }

  if (exists(bridgeSupportDylib)) {
    fw.inline = core.dlopen(bridgeSupportDylib);
  }

  var contents = fs.readFileSync(bridgeSupportXML, 'utf8')
    , parser = sax.parser(true)
    , gotEnd = false
    , classes = []
    , constants = []
    , curName
    , curRtnType
    , curArgTypes
    , isInline;
  //console.error(contents);

  parser.onerror = function (e) { throw e; }
  parser.onopentag = function (node) {
    //console.error(node);
    switch (node.name) {
      case 'depends_on':
        bridgesupport.import(node.attributes.path);
        break;
      case 'class':
        break;
      case 'method':
        break;
      case 'arg':
        if (curName) {
          // class methods also have args, we only want functions though
          curArgTypes.push(getType(node));
        }
        break;
      case 'retval':
        if (curName) {
          // class methods also have retvals, we only want functions though
          curRtnType = getType(node);
        }
        break;
      case 'signatures':
        // ignore
        break;
      case 'string_constant':
        _global[node.attributes.name] = node.attributes.value;
        break;
      case 'enum':
        _global[node.attributes.name] = parseInt(node.attributes.value);
        break;
      case 'struct':
        break;
      case 'field':
        //console.error(node);
        break;
      case 'cftype':
        break;
      case 'constant':
        //constants.push(node);
        break;
      case 'function':
        curName = node.attributes.name;
        curRtnType = null;
        curArgTypes = [];
        isInline = node.attributes.inline == 'true';
        // TODO: is variadic? will require a 'function generator'
        break;
      case 'args':
        break;
      case 'opaque':
        break;
      case 'informal_protocol':
        break;
      case 'function_alias':
        break;
      default:
        throw new Error('unkown tag: '+ node.name);
        break;
    }
  };
  parser.onclosetag = function (node) {
    switch (node) {
      case 'function':
        // Binded functions will be lazy-loaded. We simply define a getter that
        // does the creation magic the first time, and replaces the getter with
        // the binded function
        (function (curName, curRtnType, curArgTypes, isInline) {
          if (isInline && !fw.inline) throw new Error('declared inline but could not find inline dylib!');
          _global.__defineGetter__(curName, function () {
            var f = core.Function(curName, curRtnType, curArgTypes, false, isInline ? fw.inline : fw.lib);
            delete _global[curName];
            return _global[curName] = f;
          });
        })(curName, curRtnType, curArgTypes, isInline);
        curName = null;
        break;
    }
  };
  parser.onend = function () {
    gotEnd = true;
  };

  // Parse the contents of the file. This should happen synchronously.
  parser.write(contents).close();

  if (!gotEnd) {
    throw new Error('could not parse BridgeSupport files synchronously');
  }

  /*
  constants.forEach(function (node) {
    var sym = lib.get(node.attributes.name);
    if (node.attributes.declared_type == 'NSString*') {
      sym = Class.wrapId(sym);
      sym.__proto__ = Class.getClass('NSString').prototype;
      framework[node.attributes.name] = sym;
    }
  });
  */
}
module.exports = bridgesupport;

// TODO: Add 64-bit retrievial, 'type64'
function getType (node) {
  return types.map(node.attributes.type);
}
<|endoftext|>


<commit_before>'use strict';

function processAsset(asset) {
  if (asset.status <= 399)
    return 0;
  if (asset.status <= 499)
    return 10;
  return 20;
}

module.exports = {
  id: 'responseOk',
  title: 'Avoid missing and error requests',
  description: 'Check for 40X and 50X responses, and report them.',
  weight: 7,
  tags: ['performance','server'],

  processPage: function(page) {
    let score = 100;
    let errors = 0;
    let missing = 0;
    let offending = [];
    let offendingCodes = [];
    page.assets.forEach(function(asset) {
      let myScore = processAsset(asset);
      if (myScore > 0) {
        score -= myScore;
        offendingCodes.push(asset.status);
        offending.push(asset.url);
        if (myScore === 20) {
          errors++;
        }
        else {
          missing++;
        }
      }
    });

    return {
      score: Math.max(0, score),
      offending: offending,
      advice: score < 100 ? 'The page has ' + offendingCodes.length + ' error response(s). The response code(s) are ' + offendingCodes.join(' ') + '. Fix them!' : ''
    }
  }
};
<commit_msg>simplified and cleaner error advice<commit_after>'use strict';

module.exports = {
  id: 'responseOk',
  title: 'Avoid missing and error requests',
  description: 'Check for 40X and 50X responses, and report them.',
  weight: 7,
  tags: ['performance','server'],

  processPage: function(page) {
    let score = 100;
    let offending = [];
    let offendingCodes = {};
    let advice = '';
    page.assets.forEach(function(asset) {
      if (asset.status >= 400) {
        offending.push(asset.url);
        score -= 10;
        if (offendingCodes[asset.status]) {
          offendingCodes[asset.status] += 1;
        } else {
          offendingCodes[asset.status] = 1;
        }
      }
    });

    if (score < 100) {
      advice = 'The page has ' + offending.length + ' error response(s). ';
      Object.keys(offendingCodes).forEach(function(errorCode) {
        advice += 'The page has ' + offendingCodes[errorCode] + ' response(s) with code ' + errorCode + '. ';
      });
    }

    return {
      score: Math.max(0, score),
      offending: offending,
      advice: advice
    }
  }
};
<|endoftext|>


<commit_before>'use strict';

/*
 *
 * The key parameter 'pack'
 * The supplied 'custom_prelude.js' file is needed for
 * .load function of soop.
 */

var fs = require('fs');
var browserify = require('browserify');
var browserPack = require('browser-pack');
var program = require('commander');

// concat browser vendor files
var exec = require('child_process').exec;
var sys = require('sys');
var puts = function(error, stdout, stderr) {
  if (error) console.log(error);
  sys.puts(stdout);
  sys.puts(stderr);
};

exec('cd browser; sh concat.sh', puts);

var list = function(val) {
  return val.split(',');
};

program
  .version('0.0.1')
  .option('-a, --includeall', 'Include all submodules.')
  .option('-d, --dontminify', 'Don\'t minify the code.')
  .option('-s, --submodules <items>', 'Include the listed comma-separated submodules.', list)
  .parse(process.argv);

if (!program.includeall && (!program.submodules || program.submodules.length === 0)) {
  console.log('Must use either -s or -a option. For more info use the --help option');
  process.exit(1);
}

var pack = function (params) {
  var preludePath  = 'node_modules/soop/example/custom_prelude.js';
  params.raw = true;
  params.sourceMapPrefix = '//#';
  params.prelude = fs.readFileSync(preludePath, 'utf8');
  params.preludePath = preludePath;
  return browserPack(params);
};

var modules = [
  'Address',
  'Block',
  'Bloom',
  'Buffers.monkey',
  'Connection',
  'Deserialize',
  'Gruntfile',
  'Number.monkey',
  'Opcode',
  'Peer',
  'PeerManager',
  'PrivateKey',
  'RpcClient',
  'Key',
  'SIN',
  'SINKey',
  'Script',
  'ScriptInterpreter',
  'Sign',
  'Transaction',
  'Wallet',
  'WalletKey',
  'config',
  'const',
  'networks',
  'util/log',
  'util/util',
  'util/EncodedData',
  'util/VersionedData',
];

var opts = {};
opts.pack = pack;
opts.debug = true;
opts.standalone = 'bitcore';
opts.insertGlobals = true;

var b = browserify(opts);
b.require('browserify-bignum/bignumber.js', {expose: 'bignum'} );
b.require('browserify-buffertools/buffertools.js', {expose:'buffertools'});
b.require('base58-native', {expose: 'base58-native'});
b.require('./bitcore', {expose: 'bitcore'});
modules.forEach(function(m) {
  if (program.includeall || program.submodules.indexOf(m) > -1) {
    console.log('Including '+m+' in the browser bundle');
    b.require('./' + m + '.js' , {expose: './'+m} );
  }
});

if (!program.dontminify) {
  b.transform({
    global: true
  }, 'uglifyify');
}

var bundle = b.bundle();
bundle = bundle.pipe(fs.createWriteStream('browser/bundle.js'));

opts.standalone = 'testdata';
var tb = browserify(opts);
tb.require('./test/testdata', {expose: 'testdata'});
tb.transform('brfs');

tb.bundle().pipe(fs.createWriteStream('browser/testdata.js'));

<commit_msg>modularize browser build.js<commit_after>'use strict';

var fs = require('fs');
var browserify = require('browserify');
var browserPack = require('browser-pack');
var exec = require('child_process').exec;
var sys = require('sys');
var puts = function(error, stdout, stderr) {
  if (error) console.log(error);
  sys.puts(stdout);
  sys.puts(stderr);
};

var pack = function(params) {
  var preludePath = 'node_modules/soop/example/custom_prelude.js';
  params.raw = true;
  params.sourceMapPrefix = '//#';
  params.prelude = fs.readFileSync(preludePath, 'utf8');
  params.preludePath = preludePath;
  return browserPack(params);
};

var modules = [
  'Address',
  'Block',
  'Bloom',
  'Buffers.monkey',
  'Connection',
  'Deserialize',
  'Gruntfile',
  'Number.monkey',
  'Opcode',
  'Peer',
  'PeerManager',
  'PrivateKey',
  'RpcClient',
  'Key',
  'SIN',
  'SINKey',
  'Script',
  'ScriptInterpreter',
  'Sign',
  'Transaction',
  'Wallet',
  'WalletKey',
  'config',
  'const',
  'networks',
  'util/log',
  'util/util',
  'util/EncodedData',
  'util/VersionedData',
];

var createBitcore = function(opts) {

  // concat browser vendor files
  exec('cd browser; sh concat.sh', puts);

  if (!opts.includeall && (!opts.submodules || opts.submodules.length === 0)) {
    console.log('Must use either -s or -a option. For more info use the --help option');
    process.exit(1);
  }

  var bopts = {
    pack : pack,
    debug : true,
    standalone : 'bitcore',
    insertGlobals: true
  };
  var b = browserify(bopts);

  b.require('browserify-bignum/bignumber.js', {
    expose: 'bignum'
  });
  b.require('browserify-buffertools/buffertools.js', {
    expose: 'buffertools'
  });
  b.require('base58-native', {
    expose: 'base58-native'
  });
  b.require('./bitcore', {
    expose: 'bitcore'
  });
  modules.forEach(function(m) {
    if (opts.includeall || opts.submodules.indexOf(m) > -1) {
      console.log('Including ' + m + ' in the browser bundle');
      b.require('./' + m + '.js', {
        expose: './' + m
      });
    }
  });

  if (!opts.dontminify) {
    b.transform({
      global: true
    }, 'uglifyify');
  }

  var bundle = b.bundle();
  return bundle;
};

var createTestData = function() {
  var bopts = {
    pack : pack,
    debug : true,
    standalone : 'testdata',
    insertGlobals: true
  };
  var tb = browserify(bopts);
  tb.require('./test/testdata', {
    expose: 'testdata'
  });
  tb.transform('brfs');

  return tb.bundle();
};

if (require.main === module) {
  var list = function(val) {
    return val.split(',');
  };
  var program = require('commander');
  program
    .version('0.0.1')
    .option('-a, --includeall', 'Include all submodules.')
    .option('-d, --dontminify', 'Don\'t minify the code.')
    .option('-s, --submodules <items>', 'Include the listed comma-separated submodules.', list)
    .parse(process.argv);
  var bitcoreBundle = createBitcore(program);
  var testBundle = createTestData(program);

  testBundle.pipe(fs.createWriteStream('browser/testdata.js'));
  bitcoreBundle.pipe(fs.createWriteStream('browser/bundle.js'));

}
<|endoftext|>


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

'use strict';

var cls = require('../../cls.js');
var shimmer = require('shimmer');
var semver = require('semver');
var SpanData = require('../../span-data.js');
var agent;

var SUPPORTED_VERSIONS = '<=2.x';

function createClientWrap(createClient) {
  return function createClientTrace() {
    var client = createClient.apply(this, arguments);
    cls.getNamespace().bindEmitter(client);
    return client;
  };
}

// Used for redis version > 2.3
function createStreamWrap(create_stream) {
  return function create_stream_trace() {
    if (!this.stream) {
      Object.defineProperty(this, 'stream', {
        get: function () { return this._google_trace_stream; },
        set: function (val) {
          cls.getNamespace().bindEmitter(val);
          this._google_trace_stream = val;
        }
      });
    }
    return create_stream.apply(this, arguments);
  };
}

// Used for redis version <= 2.3
function streamListenersWrap(install_stream_listeners) {
  return function install_stream_listeners_trace() {
    cls.getNamespace().bindEmitter(this.stream);
    return install_stream_listeners.apply(this, arguments);
  };
}

function sendCommandWrap(send_command) {
  return function send_command_trace(cmd, args, cb) {
    var root = cls.getRootContext();
    if (!root) {
      agent.logger.debug('Untraced redis command:', cmd, args);
      return send_command.call(this, cmd, args, cb);
    } else if (root === SpanData.nullSpan) {
      return send_command.call(this, cmd, args, cb);
    }
    if (!cmd || !args || typeof cmd !== 'string' || !Array.isArray(args) ||
        (cb && typeof cb !== 'function')) {
      return send_command.call(this, cmd, args, cb);
    }
    if (!cb) {
      if (typeof args[args.length - 1] === 'function' ||
          typeof args[args.length - 1] === 'undefined') {
        cb = args.pop();
      }
    }
    var labels = { command: cmd };
    if (agent.config_.enhancedDatabaseReporting) {
      labels.arguments = JSON.stringify(args);
    }
    var span = agent.startSpan('redis-' + cmd, labels);
    return send_command.call(this, cmd, args, wrapCallback(span, cb));
  };
}

function wrapCallback(span, done) {
  var fn = function(err, res) {
    var labels = {};
    if (agent.config_.enhancedDatabaseReporting) {
      if (err) {
        labels.error = err;
      }
      if (res) {
        labels.result = res;
      }
    }
    agent.endSpan(span, labels);
    if (done) {
      done(err, res);
    }
  };
  return cls.getNamespace().bind(fn);
}

module.exports = function(version_, agent_) {
  if (!semver.satisfies(version_, SUPPORTED_VERSIONS)) {
    agent_.logger.info('Redis: unsupported version ' + version_ + ' loaded');
    return {};
  }
  return {
    // An empty relative path here matches the root module being loaded.
    '': {
      patch: function(redis) {
        agent = agent_;
        shimmer.wrap(redis.RedisClient.prototype, 'send_command', sendCommandWrap);
        if (semver.satisfies(version_, '<=2.3.x')) {
          shimmer.wrap(redis.RedisClient.prototype, 'install_stream_listeners',
            streamListenersWrap);
        } else {
          shimmer.wrap(redis.RedisClient.prototype, 'create_stream',
            createStreamWrap);
        }
        shimmer.wrap(redis, 'createClient', createClientWrap);
      },
      unpatch: function(redis) {
        shimmer.unwrap(redis.RedisClient.prototype, 'send_command');
        if (semver.satisfies(version_, '<=2.3.x')) {
          shimmer.unwrap(redis.RedisClient.prototype, 'install_stream_listeners');
        } else {
          shimmer.unwrap(redis.RedisClient.prototype, 'create_stream');
        }
        shimmer.unwrap(redis, 'createClient');
        agent_.logger.info('Redis: unpatched');
      }
    }
  };
};
<commit_msg>Add support for redis 2.6<commit_after>/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var cls = require('../../cls.js');
var shimmer = require('shimmer');
var semver = require('semver');
var SpanData = require('../../span-data.js');
var agent;

var SUPPORTED_VERSIONS = '<=2.6.x';

function createClientWrap(createClient) {
  return function createClientTrace() {
    var client = createClient.apply(this, arguments);
    cls.getNamespace().bindEmitter(client);
    return client;
  };
}

// Used for redis version > 2.3
function createStreamWrap(create_stream) {
  return function create_stream_trace() {
    if (!this.stream) {
      Object.defineProperty(this, 'stream', {
        get: function () { return this._google_trace_stream; },
        set: function (val) {
          cls.getNamespace().bindEmitter(val);
          this._google_trace_stream = val;
        }
      });
    }
    return create_stream.apply(this, arguments);
  };
}

// Used for redis version <= 2.3
function streamListenersWrap(install_stream_listeners) {
  return function install_stream_listeners_trace() {
    cls.getNamespace().bindEmitter(this.stream);
    return install_stream_listeners.apply(this, arguments);
  };
}

function setupSpan(cmd, args, skipped_frames) {
  var labels = { command: cmd };
  if (agent.config_.enhancedDatabaseReporting) {
    labels.arguments = JSON.stringify(args);
  }
  return agent.startSpan('redis-' + cmd, labels, skipped_frames + 1);
}

function startSpanFromArguments(cmd, args, cb, send_command) {
  if (!cmd || !args || typeof cmd !== 'string' || !Array.isArray(args) ||
    (cb && typeof cb !== 'function')) {
    return send_command(cmd, args, cb);
  }
  if (!cb) {
    if (typeof args[args.length - 1] === 'function' ||
        typeof args[args.length - 1] === 'undefined') {
      cb = args.pop();
    }
  }
  var span = setupSpan(cmd, args, 1);
  return send_command(cmd, args, wrapCallback(span, cb));
}

function validRootSpan(root, cmd, args) {
  if (!root) {
    agent.logger.debug('Untraced redis command:', cmd, args);
    return false;
  } else if (root === SpanData.nullSpan) {
    return false;
  }
  return true;
}

function internalSendCommandWrap(internal_send_command) {
  return function internal_send_command_trace(cmd, args, cb) {
    var root = cls.getRootContext();
    if (!validRootSpan(root, cmd, args)) {
      return internal_send_command.call(this, cmd, args, cb);
    }
    if (arguments.length === 1 && typeof cmd === 'object') {
      var span = setupSpan(cmd.command, cmd.args, 0);
      cmd.callback = wrapCallback(span, cmd.callback);
      return internal_send_command.call(this, cmd);
    }
    return startSpanFromArguments(cmd, args, cb, internal_send_command.bind(this));
  };
}

function sendCommandWrap(send_command) {
  return function send_command_trace(cmd, args, cb) {
    var root = cls.getRootContext();
    if (!validRootSpan(root, cmd, args)) {
      return send_command.call(this, cmd, args, cb);
    }
    return startSpanFromArguments(cmd, args, cb, send_command.bind(this));
  };
}

function wrapCallback(span, done) {
  var fn = function(err, res) {
    var labels = {};
    if (agent.config_.enhancedDatabaseReporting) {
      if (err) {
        labels.error = err;
      }
      if (res) {
        labels.result = res;
      }
    }
    agent.endSpan(span, labels);
    if (done) {
      done(err, res);
    }
  };
  return cls.getNamespace().bind(fn);
}

module.exports = function(version_, agent_) {
  if (!semver.satisfies(version_, SUPPORTED_VERSIONS)) {
    agent_.logger.info('Redis: unsupported version ' + version_ + ' loaded');
    return {};
  }
  return {
    // An empty relative path here matches the root module being loaded.
    '': {
      patch: function(redis) {
        agent = agent_;
        if (semver.satisfies(version_, '<2.6')) {
          shimmer.wrap(redis.RedisClient.prototype, 'send_command', sendCommandWrap);
        } else {
          shimmer.wrap(redis.RedisClient.prototype, 'internal_send_command',
            internalSendCommandWrap);
        }
        if (semver.satisfies(version_, '<=2.3.x')) {
          shimmer.wrap(redis.RedisClient.prototype, 'install_stream_listeners',
            streamListenersWrap);
        } else {
          shimmer.wrap(redis.RedisClient.prototype, 'create_stream',
            createStreamWrap);
        }
        shimmer.wrap(redis, 'createClient', createClientWrap);
      },
      unpatch: function(redis) {
        if (semver.satisfies(version_, '<2.6')) {
          shimmer.unwrap(redis.RedisClient.prototype, 'send_command');
        } else {
          shimmer.unwrap(redis.RedisClient.prototype, 'internal_send_command');
        }
        if (semver.satisfies(version_, '<=2.3.x')) {
          shimmer.unwrap(redis.RedisClient.prototype, 'install_stream_listeners');
        } else {
          shimmer.unwrap(redis.RedisClient.prototype, 'create_stream');
        }
        shimmer.unwrap(redis, 'createClient');
        agent_.logger.info('Redis: unpatched');
      }
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

const fs = require('../../file-system');
const logger = require('aurelia-logging').getLogger('Amodro');
const path = require('path');
const amodroTrace = require('../../build/amodro-trace');
const cjsTransform = require('../../build/amodro-trace/read/cjs');

let AmodroStrategy = class {

  static inject() { return ['package']; }

  constructor(pkg) {
    this.package = pkg;
  }

  applies() {
    if (!this.package.main) {
      logger.debug('This package did not specify a "main" file in package.json. Skipping');
      return false;
    }

    return true;
  }

  execute() {
    this.moduleId = this.package.getModuleId(this.package.main);

    return this.trace(this.package.name, this.moduleId)
    .then(tracedFiles => {
      logger.debug(`The package has ${tracedFiles} dependency file(s).`);

      if (tracedFiles === 0) {
        throw new Error(`Could not trace '${this.package.main}' of the '${this.package.name}' package. There were 0 traced files`);
      }

      return this.package.detectResources()
      .then(() => {
        if (tracedFiles > 1 || this.package.resources.length > 0) {
          logger.debug('Using multi file configuration for this package.');
        } else {
          logger.debug('Using single file configuration for this package.');
          this.package.main = null;
          this.package.path = path.posix.join('../node_modules/', this.package.name, this.moduleId);
        }
      });
    })
    .then(() => {
      return {
        dependencies: [this.package.getConfiguration()]
      };
    });
  }

  trace(packageName, moduleId) {
    let rootDir = path.join(process.cwd(), 'node_modules', packageName);

    let existingFilesMap = {};

    return amodroTrace(
      {
        rootDir: rootDir,
        id: moduleId,
        fileRead: function(defaultRead, id, filePath) {
          if (fs.existsSync(filePath)) {
            existingFilesMap[filePath] = true;
            let contents = fs.readFileSync(filePath).toString();

            return contents;
          }

          existingFilesMap[filePath] = false;

          return '';
        },
        readTransform: function(id, url, contents) {
          return cjsTransform(url, contents);
        }
      }
    ).then(traceResult => {
      let traced = traceResult.traced;
      let found = traced.filter(x => existingFilesMap[x.path]);

      return found.length;
    });
  }

  get name() {
    return 'Amodrotrace Strategy';
  }
};

module.exports = AmodroStrategy;
<commit_msg>fix(importer): support nodejs module default main file "index.js" when "main" is missing in package.json<commit_after>'use strict';

const fs = require('../../file-system');
const logger = require('aurelia-logging').getLogger('Amodro');
const path = require('path');
const amodroTrace = require('../../build/amodro-trace');
const cjsTransform = require('../../build/amodro-trace/read/cjs');

// nodejs default main is index.js
const DEFAULT_MAIN = 'index.js';

let AmodroStrategy = class {

  static inject() { return ['package']; }

  constructor(pkg) {
    this.package = pkg;
  }

  applies() {
    if (!this.package.main) {
      let defaultMainPath = path.join(process.cwd(), 'node_modules', this.package.name, DEFAULT_MAIN);

      if (fs.existsSync(defaultMainPath)) {
        this.package.main = DEFAULT_MAIN;
        return true;
      }

      logger.debug('This package neither specified a "main" file in package.json, ' +
                   'nor provided default ' + DEFAULT_MAIN + ' file. Skipping');
      return false;
    }

    return true;
  }

  execute() {
    this.moduleId = this.package.getModuleId(this.package.main);

    return this.trace(this.package.name, this.moduleId)
    .then(tracedFiles => {
      logger.debug(`The package has ${tracedFiles} dependency file(s).`);

      if (tracedFiles === 0) {
        throw new Error(`Could not trace '${this.package.main}' of the '${this.package.name}' package. There were 0 traced files`);
      }

      return this.package.detectResources()
      .then(() => {
        if (tracedFiles > 1 || this.package.resources.length > 0) {
          logger.debug('Using multi file configuration for this package.');
        } else {
          logger.debug('Using single file configuration for this package.');
          this.package.main = null;
          this.package.path = path.posix.join('../node_modules/', this.package.name, this.moduleId);
        }
      });
    })
    .then(() => {
      return {
        dependencies: [this.package.getConfiguration()]
      };
    });
  }

  trace(packageName, moduleId) {
    let rootDir = path.join(process.cwd(), 'node_modules', packageName);

    let existingFilesMap = {};

    return amodroTrace(
      {
        rootDir: rootDir,
        id: moduleId,
        fileRead: function(defaultRead, id, filePath) {
          if (fs.existsSync(filePath)) {
            existingFilesMap[filePath] = true;
            let contents = fs.readFileSync(filePath).toString();

            return contents;
          }

          existingFilesMap[filePath] = false;

          return '';
        },
        readTransform: function(id, url, contents) {
          return cjsTransform(url, contents);
        }
      }
    ).then(traceResult => {
      let traced = traceResult.traced;
      let found = traced.filter(x => existingFilesMap[x.path]);

      return found.length;
    });
  }

  get name() {
    return 'Amodrotrace Strategy';
  }
};

module.exports = AmodroStrategy;
<|endoftext|>


<commit_before>/**
 *  class JadeEngine
 *
 *  Engine for the Jade template language. You will need `jade` Node modules
 *  installed in order to use [[Mincer]] with `*.jade` files:
 *
 *     npm install jade
 *
 *  This is a "backend" engine of [[JstEngine]].
 *
 *  **NOTICE** Generated functions require you to have `jade` client runtime to
 *  be required:
 *
 *  ``` javascript
 *  //= require jade-runtime
 *  //= require templates/hello
 *  ```
 *
 *
 *  ##### SUBCLASS OF
 *
 *  [[Template]]
 **/

'use strict';

// 3rd-party
var _ = require('underscore');
var Jade;   // initialized later

// internal
var Template  = require('../template');
var prop      = require('../common').prop;

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

// Class constructor
var JadeEngine = module.exports = function JadeEngine() {
  Template.apply(this, arguments);
};

require('util').inherits(JadeEngine, Template);

// Check whenever coffee-script module is loaded
JadeEngine.prototype.isInitialized = function () {
  return !!Jade;
};

// Autoload coffee-script library
JadeEngine.prototype.initializeEngine = function () {
  Jade = this.require('jade');
};

// Internal (private) options storage
var options = {};

/**
 *  JadeEngine.setOptions(value) -> Void
 *  - value (Object):
 *
 *  Allows to set Jade compilation options.
 *  See Jade compilation options for details.
 *
 *  Default: `{}`.
 *
 *
 *  ##### Example
 *
 *      JadeEngine.setOptions({self: true});
 **/
JadeEngine.setOptions = function (value) {
  options = _.clone(value);
};

// Render data
JadeEngine.prototype.evaluate = function (context, locals, callback) {
  try {
    var result = Jade.compile(this.data, _.extend({}, options, {
      client:   true,
      filename: context.pathname
    }));

    callback(null, result.toString());
  } catch (err) {
    callback(err);
  }
};
<commit_msg>Fix code comment<commit_after>/**
 *  class JadeEngine
 *
 *  Engine for the Jade template language. You will need `jade` Node modules
 *  installed in order to use [[Mincer]] with `*.jade` files:
 *
 *     npm install jade
 *
 *  This is a "backend" engine of [[JstEngine]].
 *
 *  **NOTICE** Generated functions require you to have `jade` client runtime to
 *  be required:
 *
 *  ``` javascript
 *  //= require jade-runtime
 *  //= require templates/hello
 *  ```
 *
 *
 *  ##### SUBCLASS OF
 *
 *  [[Template]]
 **/

'use strict';

// 3rd-party
var _ = require('underscore');
var Jade;   // initialized later

// internal
var Template  = require('../template');
var prop      = require('../common').prop;

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

// Class constructor
var JadeEngine = module.exports = function JadeEngine() {
  Template.apply(this, arguments);
};

require('util').inherits(JadeEngine, Template);

// Check whenever jade module is loaded
JadeEngine.prototype.isInitialized = function () {
  return !!Jade;
};

// Autoload jade library
JadeEngine.prototype.initializeEngine = function () {
  Jade = this.require('jade');
};

// Internal (private) options storage
var options = {};

/**
 *  JadeEngine.setOptions(value) -> Void
 *  - value (Object):
 *
 *  Allows to set Jade compilation options.
 *  See Jade compilation options for details.
 *
 *  Default: `{}`.
 *
 *
 *  ##### Example
 *
 *      JadeEngine.setOptions({self: true});
 **/
JadeEngine.setOptions = function (value) {
  options = _.clone(value);
};

// Render data
JadeEngine.prototype.evaluate = function (context, locals, callback) {
  try {
    var result = Jade.compile(this.data, _.extend({}, options, {
      client:   true,
      filename: context.pathname
    }));

    callback(null, result.toString());
  } catch (err) {
    callback(err);
  }
};
<|endoftext|>