<commit_before>dojo.provide("dijit.form.Button");

dojo.require("dijit.form._FormWidget");
dojo.require("dijit._Container");

dojo.declare("dijit.form.Button",
	dijit.form._FormWidget,
	{
	// summary:
	//	Basically the same thing as a normal HTML button, but with special styling.
	//
	// example:
	// |	<button dojoType="dijit.form.Button" onClick="...">Hello world</button>
	//
	// example:
	// |	var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
	// |	dojo.body().appendChild(button1.domNode);

	// label: String
	//	text to display in button
	label: "",

	// showLabel: Boolean
	//	whether or not to display the text label in button
	showLabel: true,

	// iconClass: String
	//	class to apply to div in button to make it display an icon
	iconClass: "",

	type: "button",
	baseClass: "dijitButton",
	templatePath: dojo.moduleUrl("dijit.form", "templates/Button.html"),

	_onChangeMonitor: '',
	// TODO: set button's title to this.containerNode.innerText

	_onClick: function(/*Event*/ e){
		// summary: internal function to handle click actions
		if(this.disabled || this.readOnly){
			return false;
		}
		this._clicked(); // widget click actions
		return this.onClick(e); // user click actions
	},

	_onButtonClick: function(/*Event*/ e){
		// summary: callback when the user activates the button portion
		// if is activated via a keystroke, stop the event
		if(e.type!='click'){
			dojo.stopEvent(e);
		}
		if(this._onClick(e) === false){ // returning nothing is same as true
			e.preventDefault(); // needed for checkbox
		}else if(this.type=="submit" && !this.focusNode.form){ // see if a nonform widget needs to be signalled
			for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
				var widget=dijit.byNode(node);
				if(widget && typeof widget._onSubmit == "function"){
					widget._onSubmit(e);
					break;
				}
			}
		}
	},

	postCreate: function(){
		// summary:
		//	get label and set as title on button icon if necessary
		if (this.showLabel == false){
			var labelText = "";
			this.label = this.containerNode.innerHTML;
			labelText = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
			// set title attrib on iconNode
			this.titleNode.title=labelText;
			dojo.addClass(this.containerNode,"dijitDisplayNone");
		}
		dojo.setSelectable(this.focusNode, false);
		this.inherited(arguments);
	},

	onClick: function(/*Event*/ e){
		// summary: user callback for when button is clicked
		//      if type="submit", return true to perform submit
		return true;
	},

	_clicked: function(/*Event*/ e){
		// summary: internal replaceable function for when the button is clicked
	},

	setLabel: function(/*String*/ content){
		// summary: reset the label (text) of the button; takes an HTML string
		this.containerNode.innerHTML = this.label = content;
		this._layoutHack();
		if (this.showLabel == false){
			this.titleNode.title=dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
		}
	}
});

dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
	// summary: A button with a popup
	//
	// example:
	// |	<button dojoType="dijit.form.DropDownButton" label="Hello world">
	// |
...

	// |	</button>
	//
	// example:
	// |	var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
	// |	dojo.body().appendChild(button1);
	//

	baseClass : "dijitDropDownButton",

	templatePath: dojo.moduleUrl("dijit.form" , "templates/DropDownButton.html"),

	_fillContent: function(){
		// my inner HTML contains both the button contents and a drop down widget, like
		// <DropDownButton>  push me  <Menu> ... </Menu> </DropDownButton>
		// The first node is assumed to be the button content. The widget is the popup.
		if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
			//FIXME: figure out how to filter out the widget and use all remaining nodes as button
			//	content, not just nodes[0]
			var nodes = dojo.query("*", this.srcNodeRef);
			dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);

			// save pointer to srcNode so we can grab the drop down widget after it's instantiated
			this.dropDownContainer = this.srcNodeRef;
		}
	},

	startup: function(){
		if(this._started){ return; }

		// the child widget from srcNodeRef is the dropdown widget.  Insert it in the page DOM,
		// make it invisible, and store a reference to pass to the popup code.
		if(!this.dropDown){
			var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
			this.dropDown = dijit.byNode(dropDownNode);
			delete this.dropDownContainer;
		}
		dijit.popup.prepare(this.dropDown.domNode);

		this.inherited(arguments);
	},

	destroyDescendants: function(){
		if(this.dropDown){
			this.dropDown.destroyRecursive();
			delete this.dropDown;
		}
		this.inherited(arguments);
	},

	_onArrowClick: function(/*Event*/ e){
		// summary: callback when the user mouse clicks on menu popup node
		if(this.disabled || this.readOnly){ return; }
		this._toggleDropDown();
	},

	_onDropDownClick: function(/*Event*/ e){
		// on Firefox 2 on the Mac it is possible to fire onclick
		// by pressing enter down on a second element and transferring
		// focus to the DropDownButton;
		// we want to prevent opening our menu in this situation
		// and only do so if we have seen a keydown on this button;
		// e.detail != 0 means that we were fired by mouse
		var isMacFFlessThan3 = dojo.isFF && dojo.isFF < 3
			&& navigator.appVersion.indexOf("Macintosh") != -1;
		if(!isMacFFlessThan3 || e.detail != 0 || this._seenKeydown){
			this._onArrowClick(e);
		}
		this._seenKeydown = false;
	},

	_onDropDownKeydown: function(/*Event*/ e){
		this._seenKeydown = true;
	},

	_onDropDownBlur: function(/*Event*/ e){
		this._seenKeydown = false;
	},

	_onKey: function(/*Event*/ e){
		// summary: callback when the user presses a key on menu popup node
		if(this.disabled || this.readOnly){ return; }
		if(e.charOrCode == dojo.keys.DOWN_ARROW){
			if(!this.dropDown || this.dropDown.domNode.style.visibility=="hidden"){
				dojo.stopEvent(e);
				this._toggleDropDown();
			}
		}
	},

	_onBlur: function(){
		// summary: called magically when focus has shifted away from this widget and it's dropdown
		this._closeDropDown();
		// don't focus on button.  the user has explicitly focused on something else.
		this.inherited(arguments);
	},

	_toggleDropDown: function(){
		// summary: toggle the drop-down widget; if it is up, close it, if not, open it
		if(this.disabled || this.readOnly){ return; }
		dijit.focus(this.popupStateNode);
		var dropDown = this.dropDown;
		if(!dropDown){ return; }
		if(!this._opened){
			// If there's an href, then load that first, so we don't get a flicker
			if(dropDown.href && !dropDown.isLoaded){
				var self = this;
				var handler = dojo.connect(dropDown, "onLoad", function(){
					dojo.disconnect(handler);
					self._openDropDown();
				});
				dropDown._loadCheck(true);
				return;
			}else{
				this._openDropDown();
			}
		}else{
			this._closeDropDown();
		}
	},

	_openDropDown: function(){
		var dropDown = this.dropDown;
		var oldWidth=dropDown.domNode.style.width;
		var self = this;

		dijit.popup.open({
			parent: this,
			popup: dropDown,
			around: this.domNode,
			orient:
				// TODO: add user-defined positioning option, like in Tooltip.js
				this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
				: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
			onExecute: function(){
				self._closeDropDown(true);
			},
			onCancel: function(){
				self._closeDropDown(true);
			},
			onClose: function(){
				dropDown.domNode.style.width = oldWidth;
				self.popupStateNode.removeAttribute("popupActive");
				this._opened = false;
			}
		});
		if(this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
			var adjustNode = null;
			if(!this.isLeftToRight()){
				adjustNode = dropDown.domNode.parentNode;
				var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
			}
			// make menu at least as wide as the button
			dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
			if(adjustNode){
				adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
			}
		}
		this.popupStateNode.setAttribute("popupActive", "true");
		this._opened=true;
		if(dropDown.focus){
			dropDown.focus();
		}
		// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
	},

	_closeDropDown: function(/*Boolean*/ focus){
		if(this._opened){
			dijit.popup.close(this.dropDown);
			if(focus){ this.focus(); }
			this._opened = false;
		}
	}
});

dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
	// summary: A Normal Button with a DropDown
	//
	// example:
	// |	<button dojoType="dijit.form.ComboButton" onClick="...">
	// |		Hello world
	// |
...

	// |	</button>
	//
	// example:
	// |	var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
	// |	dojo.body().appendChild(button1.domNode);
	//

	templatePath: dojo.moduleUrl("dijit.form", "templates/ComboButton.html"),

	attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
		{id:"", name:""}),

	// optionsTitle: String
	//  text that describes the options menu (accessibility)
	optionsTitle: "",

	baseClass: "dijitComboButton",

	_focusedNode: null,

	postCreate: function(){
		this.inherited(arguments);
		this._focalNodes = [this.titleNode, this.popupStateNode];
		dojo.forEach(this._focalNodes, dojo.hitch(this, function(node){
			if(dojo.isIE){
				this.connect(node, "onactivate", this._onNodeFocus);
				this.connect(node, "ondeactivate", this._onNodeBlur);
			}else{
				this.connect(node, "onfocus", this._onNodeFocus);
				this.connect(node, "onblur", this._onNodeBlur);
			}
		}));
	},

	focusFocalNode: function(node){
		// summary: Focus the focal node node.
		this._focusedNode = node;
		dijit.focus(node);
	},

	hasNextFocalNode: function(){
		// summary: Returns true if this widget has no node currently
		//		focused or if there is a node following the focused one.
		//		False is returned if the last node has focus.
		return this._focusedNode !== this.getFocalNodes()[1];
	},

	focusNext: function(){
		// summary: Focus the focal node following the current node with focus
		//		or the first one if no node currently has focus.
		this._focusedNode = this.getFocalNodes()[this._focusedNode ? 1 : 0];
		dijit.focus(this._focusedNode);
	},

	hasPrevFocalNode: function(){
		// summary: Returns true if this widget has no node currently
		//		focused or if there is a node before the focused one.
		//		False is returned if the first node has focus.
		return this._focusedNode !== this.getFocalNodes()[0];
	},

	focusPrev: function(){
		// summary: Focus the focal node before the current node with focus
		//		or the last one if no node currently has focus.
		this._focusedNode = this.getFocalNodes()[this._focusedNode ? 0 : 1];
		dijit.focus(this._focusedNode);
	},

	getFocalNodes: function(){
		// summary: Returns an array of focal nodes for this widget.
		return this._focalNodes;
	},

	_onNodeFocus: function(evt){
		this._focusedNode = evt.currentTarget;
		var fnc = this._focusedNode == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
		dojo.addClass(this._focusedNode, fnc);
	},

	_onNodeBlur: function(evt){
		var fnc = evt.currentTarget == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
		dojo.removeClass(evt.currentTarget, fnc);
	},

	_onBlur: function(){
		this.inherited(arguments);
		this._focusedNode = null;
	}
});

dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
	// summary:
	//	A button that can be in two states (checked or not).
	//	Can be base class for things like tabs or checkbox or radio buttons

	baseClass: "dijitToggleButton",

	// checked: Boolean
	//		Corresponds to the native HTML <input> element's attribute.
	//		In markup, specified as "checked='checked'" or just "checked".
	//		True if the button is depressed, or the checkbox is checked,
	//		or the radio button is selected, etc.
	checked: false,

	_onChangeMonitor: 'checked',

	attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap),
		{checked:"focusNode"}),

	_clicked: function(/*Event*/ evt){
		this.setAttribute('checked', !this.checked);
	},

	setAttribute: function(/*String*/ attr, /*anything*/ value){
		this.inherited(arguments);
		switch(attr){
			case "checked":
				dijit.setWaiState(this.focusNode || this.domNode, "pressed", this.checked);
				this._setStateClass();
				this._handleOnChange(this.checked, true);
		}
	},

	setChecked: function(/*Boolean*/ checked){
		// summary:
		//	Programatically deselect the button
		dojo.deprecated("setChecked("+checked+") is deprecated. Use setAttribute('checked',"+checked+") instead.", "", "2.0");
		this.setAttribute('checked', checked);
	},

	postCreate: function(){
		this.inherited(arguments);
		this.setAttribute('checked', this.checked); //to initially set wai pressed state
	}
});
<commit_msg>fix typo.  fixes #7115.  !strict<commit_after>dojo.provide("dijit.form.Button");

dojo.require("dijit.form._FormWidget");
dojo.require("dijit._Container");

dojo.declare("dijit.form.Button",
	dijit.form._FormWidget,
	{
	// summary:
	//	Basically the same thing as a normal HTML button, but with special styling.
	//
	// example:
	// |	<button dojoType="dijit.form.Button" onClick="...">Hello world</button>
	//
	// example:
	// |	var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
	// |	dojo.body().appendChild(button1.domNode);

	// label: String
	//	text to display in button
	label: "",

	// showLabel: Boolean
	//	whether or not to display the text label in button
	showLabel: true,

	// iconClass: String
	//	class to apply to div in button to make it display an icon
	iconClass: "",

	type: "button",
	baseClass: "dijitButton",
	templatePath: dojo.moduleUrl("dijit.form", "templates/Button.html"),

	_onChangeMonitor: '',
	// TODO: set button's title to this.containerNode.innerText

	_onClick: function(/*Event*/ e){
		// summary: internal function to handle click actions
		if(this.disabled || this.readOnly){
			return false;
		}
		this._clicked(); // widget click actions
		return this.onClick(e); // user click actions
	},

	_onButtonClick: function(/*Event*/ e){
		// summary: callback when the user activates the button portion
		// if is activated via a keystroke, stop the event
		if(e.type!='click'){
			dojo.stopEvent(e);
		}
		if(this._onClick(e) === false){ // returning nothing is same as true
			e.preventDefault(); // needed for checkbox
		}else if(this.type=="submit" && !this.focusNode.form){ // see if a nonform widget needs to be signalled
			for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
				var widget=dijit.byNode(node);
				if(widget && typeof widget._onSubmit == "function"){
					widget._onSubmit(e);
					break;
				}
			}
		}
	},

	postCreate: function(){
		// summary:
		//	get label and set as title on button icon if necessary
		if (this.showLabel == false){
			var labelText = "";
			this.label = this.containerNode.innerHTML;
			labelText = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
			// set title attrib on iconNode
			this.titleNode.title=labelText;
			dojo.addClass(this.containerNode,"dijitDisplayNone");
		}
		dojo.setSelectable(this.focusNode, false);
		this.inherited(arguments);
	},

	onClick: function(/*Event*/ e){
		// summary: user callback for when button is clicked
		//      if type="submit", return true to perform submit
		return true;
	},

	_clicked: function(/*Event*/ e){
		// summary: internal replaceable function for when the button is clicked
	},

	setLabel: function(/*String*/ content){
		// summary: reset the label (text) of the button; takes an HTML string
		this.containerNode.innerHTML = this.label = content;
		this._layoutHack();
		if (this.showLabel == false){
			this.titleNode.title=dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
		}
	}
});

dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
	// summary: A button with a popup
	//
	// example:
	// |	<button dojoType="dijit.form.DropDownButton" label="Hello world">
	// |
...

	// |	</button>
	//
	// example:
	// |	var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
	// |	dojo.body().appendChild(button1);
	//

	baseClass : "dijitDropDownButton",

	templatePath: dojo.moduleUrl("dijit.form" , "templates/DropDownButton.html"),

	_fillContent: function(){
		// my inner HTML contains both the button contents and a drop down widget, like
		// <DropDownButton>  push me  <Menu> ... </Menu> </DropDownButton>
		// The first node is assumed to be the button content. The widget is the popup.
		if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
			//FIXME: figure out how to filter out the widget and use all remaining nodes as button
			//	content, not just nodes[0]
			var nodes = dojo.query("*", this.srcNodeRef);
			dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);

			// save pointer to srcNode so we can grab the drop down widget after it's instantiated
			this.dropDownContainer = this.srcNodeRef;
		}
	},

	startup: function(){
		if(this._started){ return; }

		// the child widget from srcNodeRef is the dropdown widget.  Insert it in the page DOM,
		// make it invisible, and store a reference to pass to the popup code.
		if(!this.dropDown){
			var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
			this.dropDown = dijit.byNode(dropDownNode);
			delete this.dropDownContainer;
		}
		dijit.popup.prepare(this.dropDown.domNode);

		this.inherited(arguments);
	},

	destroyDescendants: function(){
		if(this.dropDown){
			this.dropDown.destroyRecursive();
			delete this.dropDown;
		}
		this.inherited(arguments);
	},

	_onArrowClick: function(/*Event*/ e){
		// summary: callback when the user mouse clicks on menu popup node
		if(this.disabled || this.readOnly){ return; }
		this._toggleDropDown();
	},

	_onDropDownClick: function(/*Event*/ e){
		// on Firefox 2 on the Mac it is possible to fire onclick
		// by pressing enter down on a second element and transferring
		// focus to the DropDownButton;
		// we want to prevent opening our menu in this situation
		// and only do so if we have seen a keydown on this button;
		// e.detail != 0 means that we were fired by mouse
		var isMacFFlessThan3 = dojo.isFF && dojo.isFF < 3
			&& navigator.appVersion.indexOf("Macintosh") != -1;
		if(!isMacFFlessThan3 || e.detail != 0 || this._seenKeydown){
			this._onArrowClick(e);
		}
		this._seenKeydown = false;
	},

	_onDropDownKeydown: function(/*Event*/ e){
		this._seenKeydown = true;
	},

	_onDropDownBlur: function(/*Event*/ e){
		this._seenKeydown = false;
	},

	_onKey: function(/*Event*/ e){
		// summary: callback when the user presses a key on menu popup node
		if(this.disabled || this.readOnly){ return; }
		if(e.charOrCode == dojo.keys.DOWN_ARROW){
			if(!this.dropDown || this.dropDown.domNode.style.visibility=="hidden"){
				dojo.stopEvent(e);
				this._toggleDropDown();
			}
		}
	},

	_onBlur: function(){
		// summary: called magically when focus has shifted away from this widget and it's dropdown
		this._closeDropDown();
		// don't focus on button.  the user has explicitly focused on something else.
		this.inherited(arguments);
	},

	_toggleDropDown: function(){
		// summary: toggle the drop-down widget; if it is up, close it, if not, open it
		if(this.disabled || this.readOnly){ return; }
		dijit.focus(this.popupStateNode);
		var dropDown = this.dropDown;
		if(!dropDown){ return; }
		if(!this._opened){
			// If there's an href, then load that first, so we don't get a flicker
			if(dropDown.href && !dropDown.isLoaded){
				var self = this;
				var handler = dojo.connect(dropDown, "onLoad", function(){
					dojo.disconnect(handler);
					self._openDropDown();
				});
				dropDown._loadCheck(true);
				return;
			}else{
				this._openDropDown();
			}
		}else{
			this._closeDropDown();
		}
	},

	_openDropDown: function(){
		var dropDown = this.dropDown;
		var oldWidth=dropDown.domNode.style.width;
		var self = this;

		dijit.popup.open({
			parent: this,
			popup: dropDown,
			around: this.domNode,
			orient:
				// TODO: add user-defined positioning option, like in Tooltip.js
				this.isLeftToRight() ? {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'}
				: {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'},
			onExecute: function(){
				self._closeDropDown(true);
			},
			onCancel: function(){
				self._closeDropDown(true);
			},
			onClose: function(){
				dropDown.domNode.style.width = oldWidth;
				self.popupStateNode.removeAttribute("popupActive");
				self._opened = false;
			}
		});
		if(this.domNode.offsetWidth > dropDown.domNode.offsetWidth){
			var adjustNode = null;
			if(!this.isLeftToRight()){
				adjustNode = dropDown.domNode.parentNode;
				var oldRight = adjustNode.offsetLeft + adjustNode.offsetWidth;
			}
			// make menu at least as wide as the button
			dojo.marginBox(dropDown.domNode, {w: this.domNode.offsetWidth});
			if(adjustNode){
				adjustNode.style.left = oldRight - this.domNode.offsetWidth + "px";
			}
		}
		this.popupStateNode.setAttribute("popupActive", "true");
		this._opened=true;
		if(dropDown.focus){
			dropDown.focus();
		}
		// TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
	},

	_closeDropDown: function(/*Boolean*/ focus){
		if(this._opened){
			dijit.popup.close(this.dropDown);
			if(focus){ this.focus(); }
			this._opened = false;
		}
	}
});

dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
	// summary: A Normal Button with a DropDown
	//
	// example:
	// |	<button dojoType="dijit.form.ComboButton" onClick="...">
	// |		Hello world
	// |
...

	// |	</button>
	//
	// example:
	// |	var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
	// |	dojo.body().appendChild(button1.domNode);
	//

	templatePath: dojo.moduleUrl("dijit.form", "templates/ComboButton.html"),

	attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
		{id:"", name:""}),

	// optionsTitle: String
	//  text that describes the options menu (accessibility)
	optionsTitle: "",

	baseClass: "dijitComboButton",

	_focusedNode: null,

	postCreate: function(){
		this.inherited(arguments);
		this._focalNodes = [this.titleNode, this.popupStateNode];
		dojo.forEach(this._focalNodes, dojo.hitch(this, function(node){
			if(dojo.isIE){
				this.connect(node, "onactivate", this._onNodeFocus);
				this.connect(node, "ondeactivate", this._onNodeBlur);
			}else{
				this.connect(node, "onfocus", this._onNodeFocus);
				this.connect(node, "onblur", this._onNodeBlur);
			}
		}));
	},

	focusFocalNode: function(node){
		// summary: Focus the focal node node.
		this._focusedNode = node;
		dijit.focus(node);
	},

	hasNextFocalNode: function(){
		// summary: Returns true if this widget has no node currently
		//		focused or if there is a node following the focused one.
		//		False is returned if the last node has focus.
		return this._focusedNode !== this.getFocalNodes()[1];
	},

	focusNext: function(){
		// summary: Focus the focal node following the current node with focus
		//		or the first one if no node currently has focus.
		this._focusedNode = this.getFocalNodes()[this._focusedNode ? 1 : 0];
		dijit.focus(this._focusedNode);
	},

	hasPrevFocalNode: function(){
		// summary: Returns true if this widget has no node currently
		//		focused or if there is a node before the focused one.
		//		False is returned if the first node has focus.
		return this._focusedNode !== this.getFocalNodes()[0];
	},

	focusPrev: function(){
		// summary: Focus the focal node before the current node with focus
		//		or the last one if no node currently has focus.
		this._focusedNode = this.getFocalNodes()[this._focusedNode ? 0 : 1];
		dijit.focus(this._focusedNode);
	},

	getFocalNodes: function(){
		// summary: Returns an array of focal nodes for this widget.
		return this._focalNodes;
	},

	_onNodeFocus: function(evt){
		this._focusedNode = evt.currentTarget;
		var fnc = this._focusedNode == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
		dojo.addClass(this._focusedNode, fnc);
	},

	_onNodeBlur: function(evt){
		var fnc = evt.currentTarget == this.focusNode ? "dijitDownArrowButtonFocused" : "dijitButtonContentsFocused";
		dojo.removeClass(evt.currentTarget, fnc);
	},

	_onBlur: function(){
		this.inherited(arguments);
		this._focusedNode = null;
	}
});

dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
	// summary:
	//	A button that can be in two states (checked or not).
	//	Can be base class for things like tabs or checkbox or radio buttons

	baseClass: "dijitToggleButton",

	// checked: Boolean
	//		Corresponds to the native HTML <input> element's attribute.
	//		In markup, specified as "checked='checked'" or just "checked".
	//		True if the button is depressed, or the checkbox is checked,
	//		or the radio button is selected, etc.
	checked: false,

	_onChangeMonitor: 'checked',

	attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap),
		{checked:"focusNode"}),

	_clicked: function(/*Event*/ evt){
		this.setAttribute('checked', !this.checked);
	},

	setAttribute: function(/*String*/ attr, /*anything*/ value){
		this.inherited(arguments);
		switch(attr){
			case "checked":
				dijit.setWaiState(this.focusNode || this.domNode, "pressed", this.checked);
				this._setStateClass();
				this._handleOnChange(this.checked, true);
		}
	},

	setChecked: function(/*Boolean*/ checked){
		// summary:
		//	Programatically deselect the button
		dojo.deprecated("setChecked("+checked+") is deprecated. Use setAttribute('checked',"+checked+") instead.", "", "2.0");
		this.setAttribute('checked', checked);
	},

	postCreate: function(){
		this.inherited(arguments);
		this.setAttribute('checked', this.checked); //to initially set wai pressed state
	}
});
<|endoftext|>


<commit_before>"use strict"

var matrixToCSS = require("matrix-to-css")
var shell = require("gl-now")({clearColor: [0.2, 0.4, 0.8, 1.0]})
var camera = require("game-shell-orbit-camera")(shell)
var renderText = require("gl-render-text")
var mat4 = require("gl-mat4")
var mat3 = require("gl-mat3")
var vec3 = require("gl-vec3")
var quat = require("gl-matrix-quat")
var createMesh = require("gl-mesh")
var glslify = require("glslify")

var texture
var positions = new Array(100)
var mesh
var shader

var domElement = document.createElement('div')
domElement.style.transformStyle = 'preserve-3d'
domElement.style.overflow = 'hidden'
domElement.style.pointerEvents = 'none'
domElement.style.position = 'absolute'
domElement.style.zIndex = '1' // above WebGL canvas

var cameraElement = document.createElement('div')
cameraElement.style.position = 'absolute'
cameraElement.style.transformStyle = 'preserve-3d'
//cameraElement.style.display = 'none'
//cameraElement.style.pointerEvents = 'auto' // allow mouse interaction

var iframe = document.createElement('div') // TODO: change back to iframe
//iframe.src = 'http://browserify.org'
//iframe.src = = 'data:text/html,<body bgcolor=purple>'
iframe.style.backgroundColor = 'purple'
iframe.style.width = '100%'
iframe.style.height = '100%'
cameraElement.appendChild(iframe)

domElement.appendChild(cameraElement)
document.body.appendChild(domElement)

shell.on("gl-init", function() {
  var gl = shell.gl
  texture = renderText(gl, "Billboard")

  mesh = createMesh(gl,
      [
      [0, 1, 2],
      [3, 1, 2]
       ],
      { "position": [
        [-1, -1, 0],
        [-1, 1, 0],
        [1, -1, 0],
        [1, 1, 0]] })

  shader = glslify({
    inline: true,
    vertex: "/* voxel-decals vertex shader */\
attribute vec3 position;\
\
uniform mat4 projViewModel;\
varying vec4 vColor;\
\
void main() {\
  gl_Position = projViewModel * vec4(position, 1.0);\
}",

  fragment: "/* voxel-decals fragment shader */\
precision highp float;\
varying vec4 vColor;\
\
void main() {\
  gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\
}"})(gl)

})

var model = mat4.create()

var projViewModel = mat4.create()

var scratch0 = new Float32Array(16)
var scratch1 = new Float32Array(16)

// based on orbit-camera
function simpleCameraView(out) {
  if (!out) out = mat4.create()

  var quaternion = [0,1,0,0]
  var translation = [0,0,-1]
  mat4.fromRotationTranslation(out, quaternion, translation)

  return out
}

var cameraFOVdegrees = 45
var cameraFOVradians = cameraFOVdegrees * Math.PI / 180

window.setInterval(function() {
  cameraElement.style.display = (cameraElement.style.display === 'none' ? '' : 'none')
}, 500)

shell.on("gl-render", function() {
  var proj = mat4.perspective(mat4.create(), cameraFOVradians, shell.width/shell.height, 0.1, 1000.0)
  var view = camera.view()
  //var view = simpleCameraView()

  shader.bind()
  shader.attributes.position.location = 0

  mat4.multiply(projViewModel, proj, view)
  mat4.multiply(projViewModel, projViewModel, model)

  shader.uniforms.projViewModel = projViewModel

  mesh.bind(shader)
  mesh.draw()
  mesh.unbind()

  // CSS world perspective TODO: only on gl-resize -- this doesn't change often
  var fovPx = 0.5 / Math.tan(cameraFOVradians / 2) * shell.height
  domElement.style.perspective = fovPx + 'px'
  //domElement.style.perspectiveOrigin = '50% 50%' // already is the default
  domElement.style.width = shell.width + 'px'
  domElement.style.height = shell.height + 'px'

  // CSS cameraElement
  //cameraElement.style.transform = 'translateZ(' + fovPx + 'px) ' + matrixToCSS(view) + ' translate3d(' + (shell.width/2) + 'px, ' + (shell.height/2) + 'px, 0)'
  //var matrixWorldInverse = mat4.create()
  //mat4.invert(matrixWorldInverse, view)
  //mat4.transpose(matrixWorldInverse, matrixWorldInverse)
  cameraElement.style.width = shell.width + 'px'
  cameraElement.style.height = shell.height + 'px'

  // three.js CSS3Renderer getCameraCSSMatrix inverts these to fix flipped rotation orientation
  // TODO: matrix transformation instead?
  var cssMatrix = mat4.clone(view)
  cssMatrix[1] = -cssMatrix[1]
  cssMatrix[5] = -cssMatrix[5]
  cssMatrix[9] = -cssMatrix[9]
  cssMatrix[13] = -cssMatrix[13]

  /*
  var percentBorder = 0.05
  var planeWidth = 1
  var planeHeight = 1
  var scaleX = (1 + percentBorder) * (shell.width / planeWidth)
  var scaleY = (1 + percentBorder) * (shell.height / planeHeight)
  var scaleZ = 1
  mat4.scale(cssMatrix, cssMatrix, [scaleX, scaleY, scaleZ])
  */

  cameraElement.style.transform = 'translate3d(0,0,'+fovPx+'px) ' + matrixToCSS(cssMatrix) + ' translate3d('+(shell.width/2)+'px, '+(shell.height/2)+'px, 0)'
})
<commit_msg>Scale CSS element to 1/ shell dimensions<commit_after>"use strict"

var matrixToCSS = require("matrix-to-css")
var shell = require("gl-now")({clearColor: [0.2, 0.4, 0.8, 1.0]})
var camera = require("game-shell-orbit-camera")(shell)
var renderText = require("gl-render-text")
var mat4 = require("gl-mat4")
var mat3 = require("gl-mat3")
var vec3 = require("gl-vec3")
var quat = require("gl-matrix-quat")
var createMesh = require("gl-mesh")
var glslify = require("glslify")

var texture
var positions = new Array(100)
var mesh
var shader

var domElement = document.createElement('div')
domElement.style.transformStyle = 'preserve-3d'
domElement.style.overflow = 'hidden'
domElement.style.pointerEvents = 'none'
domElement.style.position = 'absolute'
domElement.style.zIndex = '1' // above WebGL canvas

var cameraElement = document.createElement('div')
cameraElement.style.position = 'absolute'
cameraElement.style.transformStyle = 'preserve-3d'
//cameraElement.style.display = 'none'
//cameraElement.style.pointerEvents = 'auto' // allow mouse interaction

var iframe = document.createElement('div') // TODO: change back to iframe
//iframe.src = 'http://browserify.org'
//iframe.src = = 'data:text/html,<body bgcolor=purple>'
iframe.style.backgroundColor = 'purple'
iframe.style.width = '100%'
iframe.style.height = '100%'
cameraElement.appendChild(iframe)

domElement.appendChild(cameraElement)
document.body.appendChild(domElement)

shell.on("gl-init", function() {
  var gl = shell.gl
  texture = renderText(gl, "Billboard")

  mesh = createMesh(gl,
      [
      [0, 1, 2],
      [3, 1, 2]
       ],
      { "position": [
        [-1, -1, 0],
        [-1, 1, 0],
        [1, -1, 0],
        [1, 1, 0]] })

  shader = glslify({
    inline: true,
    vertex: "/* voxel-decals vertex shader */\
attribute vec3 position;\
\
uniform mat4 projViewModel;\
varying vec4 vColor;\
\
void main() {\
  gl_Position = projViewModel * vec4(position, 1.0);\
}",

  fragment: "/* voxel-decals fragment shader */\
precision highp float;\
varying vec4 vColor;\
\
void main() {\
  gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\
}"})(gl)

})

var model = mat4.create()

var projViewModel = mat4.create()

var scratch0 = new Float32Array(16)
var scratch1 = new Float32Array(16)

// based on orbit-camera
function simpleCameraView(out) {
  if (!out) out = mat4.create()

  var quaternion = [0,1,0,0]
  var translation = [0,0,-1]
  mat4.fromRotationTranslation(out, quaternion, translation)

  return out
}

var cameraFOVdegrees = 45
var cameraFOVradians = cameraFOVdegrees * Math.PI / 180

window.setInterval(function() {
  cameraElement.style.display = (cameraElement.style.display === 'none' ? '' : 'none')
}, 500)

shell.on("gl-render", function() {
  var proj = mat4.perspective(mat4.create(), cameraFOVradians, shell.width/shell.height, 0.1, 1000.0)
  var view = camera.view()
  //var view = simpleCameraView()

  shader.bind()
  shader.attributes.position.location = 0

  mat4.multiply(projViewModel, proj, view)
  mat4.multiply(projViewModel, projViewModel, model)

  shader.uniforms.projViewModel = projViewModel

  mesh.bind(shader)
  mesh.draw()
  mesh.unbind()

  // CSS world perspective TODO: only on gl-resize -- this doesn't change often
  var fovPx = 0.5 / Math.tan(cameraFOVradians / 2) * shell.height
  domElement.style.perspective = fovPx + 'px'
  //domElement.style.perspectiveOrigin = '50% 50%' // already is the default
  domElement.style.width = shell.width + 'px'
  domElement.style.height = shell.height + 'px'

  // CSS cameraElement
  //cameraElement.style.transform = 'translateZ(' + fovPx + 'px) ' + matrixToCSS(view) + ' translate3d(' + (shell.width/2) + 'px, ' + (shell.height/2) + 'px, 0)'
  //var matrixWorldInverse = mat4.create()
  //mat4.invert(matrixWorldInverse, view)
  //mat4.transpose(matrixWorldInverse, matrixWorldInverse)
  cameraElement.style.width = shell.width + 'px'
  cameraElement.style.height = shell.height + 'px'

  // three.js CSS3Renderer getCameraCSSMatrix inverts these to fix flipped rotation orientation
  var cssMatrix = mat4.clone(view)
  mat4.scale(cssMatrix, cssMatrix, [1/shell.width, 1/shell.height, 1])
  // TODO: matrix transformation instead?
  cssMatrix[1] = -cssMatrix[1]
  cssMatrix[5] = -cssMatrix[5]
  cssMatrix[9] = -cssMatrix[9]
  cssMatrix[13] = -cssMatrix[13]

  /*
  var percentBorder = 0.05
  var planeWidth = 1
  var planeHeight = 1
  var scaleX = (1 + percentBorder) * (shell.width / planeWidth)
  var scaleY = (1 + percentBorder) * (shell.height / planeHeight)
  var scaleZ = 1
  mat4.scale(cssMatrix, cssMatrix, [scaleX, scaleY, scaleZ])
  */

  cameraElement.style.transform = 'translate3d(0,0,'+fovPx+'px) ' + matrixToCSS(cssMatrix) //+ ' translate3d('+(shell.width/2)+'px, '+(shell.height/2)+'px, 0)'
})
<|endoftext|>


<commit_before>'use strict';

const chai = require('chai');

chai.should();

const Sortie = require('../lib/Sortie.js');
const mdConfig = require('./data/markdown.json');

describe('Sortie', function () {
  describe('#constructor()', function () {
    it('should throw TypeError when called with no argument or an invalid argument', function () {
      (() => { new Sortie(); }).should.throw(TypeError);
      (() => { new Sortie({}); }).should.throw(TypeError);
    });
  });

  describe('#toString()', function () {
    const testData = {
      _id: { $id: '1235sdgas' },
      Activation: { sec: 1000 },
      Expiry: { sec: 123124 },
      Variants: [],
    };
    const timeDate = {
      fromNow: () => -1,
      timeDeltaToString: () => 'timeDelta',
    };
    const translator = {
      languageString: s => s,
    };
    it('should format the string correctly according to the data', function () {
      const s = new Sortie(testData, { mdConfig, timeDate, translator });
      s.variants[0] = { boss: 'theBoss' };
      console.log(s.toString());
      s.toString().should.contain('no sortie');

      timeDate.fromNow = () => 1;

      s.toString().should.contain('ends in');
    });
  });
});
<commit_msg>Update sortie_test.js<commit_after>'use strict';

const chai = require('chai');

chai.should();

const Sortie = require('../lib/Sortie.js');
const mdConfig = require('./data/markdown.json');

describe('Sortie', function () {
  describe('#constructor()', function () {
    it('should throw TypeError when called with no argument or an invalid argument', function () {
      (() => { new Sortie(); }).should.throw(TypeError);
      (() => { new Sortie({}); }).should.throw(TypeError);
    });
  });

  describe('#toString()', function () {
    const testData = {
      _id: { $id: '1235sdgas' },
      Activation: { sec: 1000 },
      Expiry: { sec: 123124 },
      Variants: [],
      Boss: 'theBoss',
    };
    const timeDate = {
      fromNow: () => -1,
      timeDeltaToString: () => 'timeDelta',
    };
    const translator = {
      languageString: s => s,
    };
    it('should format the string correctly according to the data', function () {
      const s = new Sortie(testData, { mdConfig, timeDate, translator });
      console.log(s.toString());
      s.toString().should.contain('no sortie');

      timeDate.fromNow = () => 1;

      s.toString().should.contain('ends in');
    });
  });
});
<|endoftext|>


<commit_before>var Scheduler  = require('./src/scheduler');

var req0 =
{
  process: 'A',
  pageNumber: 1,
  mode: "read"
};

var req1 =
{
  process: 'A',
  pageNumber: 2,
  mode: "read"
};

var req2 =
{
  process: 'B',
  pageNumber: 2,
  mode: "read"
};

var req3 =
{
  process: 'A',
  pageNumber: 3,
  mode: "read"
};

var req4 =
{
  process: 'C',
  pageNumber: 1,
  mode: "read"
};

var reqs = [req0, req1, req2, req0, req3, req4, req4];

var sams = new Scheduler();
sams.setAlgorithm("fifo");
sams.setMemorySize(3);
sams.addRequirements(reqs);
var instants = sams.run();
console.log(instants);
<commit_msg>Changed demo to show the last changes.<commit_after>var Scheduler  = require('./src/scheduler');

var req0 =
{
  process: 'A',
  pageNumber: 1,
  mode: "read"
};

var req0M =
{
  process: 'A',
  pageNumber: 1,
  mode: "write"
};

var req1 =
{
  process: 'A',
  pageNumber: 2,
  mode: "read"
};

var req2 =
{
  process: 'B',
  pageNumber: 2,
  mode: "read"
};

var req3 =
{
  process: 'A',
  pageNumber: 3,
  mode: "read"
};

var req4 =
{
  process: 'C',
  pageNumber: 1,
  mode: "read"
};

var reqs = [req0, req0M, req1, req2, req0, req3, req4];

var sams = new Scheduler();
sams.setAlgorithm("lru");
sams.setSecondChanceReplacementPolicy(true);
sams.setAsyncFlushReplacementPolicy(true);
sams.setMemorySize(3);
sams.addRequirements(reqs);
var instants = sams.run();
instants.forEach(function(moment, index) {
  console.log("Moment: " + index);
  console.log(moment);
});
<|endoftext|>


<commit_before>(function () {
    var FourthWall = {};

    // http://css-tricks.com/snippets/javascript/get-url-variables/
    FourthWall.getQueryVariable = function (variable) {
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (var i = 0; i < vars.length; i++) {
            var pair = vars[i].split("=");
            if (pair[0] === variable) {
                return pair[1];}
            }
        return false;
    };

    FourthWall.getToken = function (hostname) {
        var token = FourthWall.getQueryVariable(hostname+'_token');
        if (token === false && hostname == 'api.github.com') {
            token = FourthWall.getQueryVariable('token');
        }
        return token;
    };

    FourthWall.getTokenFromUrl = function (url) {
        var a = document.createElement('a');
        a.href = url;
        return FourthWall.getToken(a.hostname);
    };

    var overrideFetch = function(url) {
        return Backbone.Model.prototype.fetch.apply(this, [{
            beforeSend: setupAuthentication(url)
        }]);
    };

    var setupAuthentication = function (baseUrl) {
        return function(xhr) {
            xhr.setRequestHeader('Authorization', 'token ' + FourthWall.getTokenFromUrl(baseUrl))
        };
    };

    var gistId = FourthWall.getQueryVariable('gist');
    // hack for SimpleHTTPServer appending a slash
    if (gistId[gistId.length-1] == '/') {
        gistId = gistId.substr(0, gistId.length - 1);
    }

    FourthWall.Comment = Backbone.Model.extend({
        parse: function (response) {
            var thumbsup = response.some(function(comment) {
                var checkFor = [":+1:", ":thumbsup:"];
                return checkFor.some(function(check) {
                    return comment.body.indexOf(check) != -1;
                })
            });
            return {
                thumbsup: thumbsup,
                numComments: response.length
            }
        },
        fetch: function() {
            return overrideFetch.call(this, this.url);
        }
    });

    FourthWall.Status = Backbone.Model.extend({

        initialize: function () {
            this.on('change:sha', function () {
                this.fetch();
            }, this);
        },

        url: function () {
            return [
                this.get('baseUrl'),
                this.get('userName'),
                this.get('repo'),
                'statuses',
                this.get('sha')
            ].join('/')
        },

        fetch: function() {
            return overrideFetch.call(this, this.get('baseUrl'));
        },

        parse: function (response) {
            if (!response.length) {
                return;
            }
            var data = response[0];
            data.created_at = moment(data.created_at);
            data.failed = data.state !== 'success' && data.state !== 'pending';
            return data;
        }
    });

    FourthWall.MasterStatus = FourthWall.Status.extend({
        url: function () {
            return [
                this.get('baseUrl'),
                this.get('userName'),
                this.get('repo'),
                'statuses',
                'master'
            ].join('/')
        }
    });

    FourthWall.Repo = Backbone.Model.extend({
        defaults: {
            'baseUrl': 'https://api.github.com/repos'
        },

        initialize: function () {
            this.master = new FourthWall.MasterStatus({
                baseUrl: this.get('baseUrl'),
                userName: this.get('userName'),
                repo: this.get('repo')
            });

            this.master.on('change:failed', function () {
                this.trigger('change');
            }, this);

            this.pulls = new FourthWall.Pulls([], {
                baseUrl: this.get('baseUrl'),
                userName: this.get('userName'),
                repo: this.get('repo')
            });

            this.pulls.on('reset add remove', function () {
                this.trigger('change');
            }, this);
        },

        fetch: function () {
            this.pulls.fetch();
            this.master.fetch();
        }

    });

    FourthWall.Repos = Backbone.Collection.extend({

        model: FourthWall.Repo,

        initialize: function () {
          this.on('reset add remove', function () {
            this.fetch();

            this.each(function (model) {
                model.off();
                model.on('change', function () {
                    this.trigger('change');
                }, this);
            }, this);
          }, this);
        },

        schedule: function () {
            var listInterval = FourthWall.getQueryVariable('listinterval') || 900;
            var statusInterval = FourthWall.getQueryVariable('interval') || 60;
            this.updateList();
            setInterval(_.bind(function () {
              this.updateList();
            }, this), listInterval * 1000);
            setInterval(_.bind(function () {
              this.fetch();
            }, this), statusInterval * 1000);
        },

        updateList: function () {
            var that = this;
            $.ajax({
                url: 'https://api.github.com/gists/' + gistId + '?access_token=' + FourthWall.getToken('api.github.com'),
                type: 'GET',
                dataType: 'jsonp',
                success: function (gistdata) {
                    var objects = [];
                    for (file in gistdata.data.files) {
                        if (gistdata.data.files.hasOwnProperty(file)) {
                            var filedata = gistdata.data.files[file],
                                lang = filedata.language;

                            if (lang == 'JavaScript' || lang == 'JSON' || lang == null) {
                                var o = JSON.parse(filedata.content);
                                if (o) {
                                    objects.push(o);
                                }
                            }
                            if (lang == 'CSS') {
                                var $custom_css = $('


<commit_before>'use strict';

const assert = require('assert');
const Taxjar = require('../dist/taxjar');

const rateMock = require('./mocks/rates');
const taxMock = require('./mocks/taxes');
const orderMock = require('./mocks/orders');
const refundMock = require('./mocks/refunds');
const nexusRegionMock = require('./mocks/nexus_regions');
const validationMock = require('./mocks/validations');
const summaryRateMock = require('./mocks/summary_rates');

let taxjarClient = new Taxjar({
  apiKey: 'test123',
  apiUrl: 'https://mockapi.taxjar.com'
});

describe('TaxJar API', () => {

  describe('client', () => {

    it('instantiates client with API token', () => {
      assert(taxjarClient, 'no client');
    });

    it('returns error with no API token', () => {
      assert.throws(() => {
        taxjarClient = new Taxjar();
      }, /Please provide a TaxJar API key/);
    });

    it('rejects promise on API error', () => {
      const errorMocks = require('./mocks/errors');

      taxjarClient.categories().catch(err => {
        assert.equal(err instanceof Error, true);
        assert.deepEqual(err, errorMocks.CATEGORY_ERROR_RES);
        assert.equal(err.detail, "Not authorized for route 'GET /v2/categories'");
        assert.equal(err.status, 401);
      });
    });

  });

  describe('categories', () => {

    it('lists tax categories', () => {
      const categoryMock = require('./mocks/categories');

      taxjarClient.categories().then(res => {
        assert(res, 'no categories');
        assert.deepEqual(res, categoryMock.CATEGORY_RES);
      });
    });

  });

  describe('rates', () => {

    it('shows tax rates for a location', () => {
      taxjarClient.ratesForLocation('90002').then(res => {
        assert(res, 'no rates');
        assert.deepEqual(res, rateMock.RATE_RES);
      });
    });

    it('shows tax rates for a location with additional params', () => {
      taxjarClient.ratesForLocation('90002', {
        city: 'Los Angeles',
        country: 'US'
      }).then(res => {
        assert(res, 'no rates');
        assert.deepEqual(res, rateMock.RATE_RES);
      });
    });

  });

  describe('taxes', () => {

    it('calculates sales tax for an order', () => {
      taxjarClient.taxForOrder({
        'from_country': 'US',
        'from_zip': '07001',
        'from_state': 'NJ',
        'to_country': 'US',
        'to_zip': '07446',
        'to_state': 'NJ',
        'amount': 16.50,
        'shipping': 1.5
      }).then(res => {
        assert.deepEqual(res, taxMock.TAX_RES);
      });
    });

  });

  describe('transactions', () => {

    it('lists order transactions', () => {
      taxjarClient.listOrders({
        'from_transaction_date': '2015/05/01',
        'to_transaction_date': '2015/05/31'
      }).then(res => {
        assert.deepEqual(res, orderMock.LIST_ORDER_RES);
      });
    });

    it('shows an order transaction', () => {
      taxjarClient.showOrder('123').then(res => {
        assert.deepEqual(res, orderMock.SHOW_ORDER_RES);
      });
    });

    it('creates an order transaction', () => {
      taxjarClient.createOrder({
        'transaction_id': '123',
        'transaction_date': '2015/05/14',
        'to_country': 'US',
        'to_zip': '90002',
        'to_state': 'CA',
        'to_city': 'Los Angeles',
        'to_street': '123 Palm Grove Ln',
        'amount': 17.45,
        'shipping': 1.5,
        'sales_tax': 0.95,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-9',
            'description': 'Fuzzy Widget',
            'unit_price': 15.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, orderMock.CREATE_ORDER_RES);
      });
    });

    it('updates an order transaction', () => {
      taxjarClient.updateOrder({
        'transaction_id': '123',
        'amount': 17.45,
        'shipping': 1.5,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-0',
            'description': 'Heavy Widget',
            'unit_price': 15.0,
            'discount': 0.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, orderMock.UPDATE_ORDER_RES);
      });
    });

    it('deletes an order transaction', () => {
      taxjarClient.deleteOrder('123').then(res => {
        assert.deepEqual(res, orderMock.DELETE_ORDER_RES);
      });
    });

    it('lists refund transactions', () => {
      taxjarClient.listRefunds({
        'from_transaction_date': '2015/05/01',
        'to_transaction_date': '2015/05/31'
      }).then(res => {
        assert.deepEqual(res, refundMock.LIST_REFUND_RES);
      });
    });

    it('shows a refund transaction', () => {
      taxjarClient.showRefund('321').then(res => {
        assert.deepEqual(res, refundMock.SHOW_REFUND_RES);
      });
    });

    it('creates a refund transaction', () => {
      taxjarClient.createRefund({
        'transaction_id': '123',
        'transaction_date': '2015/05/14',
        'transaction_reference_id': '123',
        'to_country': 'US',
        'to_zip': '90002',
        'to_state': 'CA',
        'to_city': 'Los Angeles',
        'to_street': '123 Palm Grove Ln',
        'amount': 17.45,
        'shipping': 1.5,
        'sales_tax': 0.95,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-9',
            'description': 'Fuzzy Widget',
            'unit_price': 15.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, refundMock.CREATE_REFUND_RES);
      });
    });

    it('updates a refund transaction', () => {
      taxjarClient.updateRefund({
        'transaction_id': '321',
        'amount': 17.95,
        'shipping': 2.0,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-0',
            'description': 'Heavy Widget',
            'unit_price': 15.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, refundMock.UPDATE_REFUND_RES);
      });
    });

    it('deletes a refund transaction', () => {
      taxjarClient.deleteRefund('321').then(res => {
        assert.deepEqual(res, refundMock.DELETE_REFUND_RES);
      });
    });

  });

  describe('nexus', () => {

    it('lists nexus regions', () => {
      taxjarClient.nexusRegions().then(res => {
        assert.deepEqual(res, nexusRegionMock.NEXUS_REGIONS_RES);
      });
    });

  });

  describe('validations', () => {

    it('validates a VAT number', () => {
      taxjarClient.validate({
        vat: 'FR40303265045'
      }).then(res => {
        assert.deepEqual(res, validationMock.VALIDATION_RES);
      });
    });

  });

  describe('summarized rates', () => {

    it('lists summarized rates', () => {
      taxjarClient.summaryRates().then(res => {
        assert.deepEqual(res, summaryRateMock.SUMMARY_RATES_RES);
      });
    });

  });

});
<commit_msg>Add specs, use Chai for assertions<commit_after>'use strict';

const assert = require('chai').assert;
const Taxjar = require('../dist/taxjar');

const rateMock = require('./mocks/rates');
const taxMock = require('./mocks/taxes');
const orderMock = require('./mocks/orders');
const refundMock = require('./mocks/refunds');
const nexusRegionMock = require('./mocks/nexus_regions');
const validationMock = require('./mocks/validations');
const summaryRateMock = require('./mocks/summary_rates');

let taxjarClient = new Taxjar({
  apiKey: 'test123',
  apiUrl: 'https://mockapi.taxjar.com'
});

describe('TaxJar API', () => {

  describe('client', () => {

    it('instantiates client with API token', () => {
      assert(taxjarClient, 'no client');
    });

    it('returns error with no API token', () => {
      assert.throws(() => {
        taxjarClient = new Taxjar();
      }, /Please provide a TaxJar API key/);
    });

    it('rejects promise on API error', () => {
      const errorMocks = require('./mocks/errors');

      taxjarClient.categories().catch(err => {
        assert.equal(err instanceof Error, true);
        assert.deepEqual(err, errorMocks.CATEGORY_ERROR_RES);
        assert.equal(err.detail, "Not authorized for route 'GET /v2/categories'");
        assert.equal(err.status, 401);
      });
    });

    it('gets api config', () => {
      assert.equal(taxjarClient.getApiConfig('apiUrl'), 'https://mockapi.taxjar.com/v2/');
    });

    it('sets api config', () => {
      taxjarClient.setApiConfig('apiUrl', 'https://api.sandbox.taxjar.com');
      assert.equal(taxjarClient.getApiConfig('apiUrl'), 'https://api.sandbox.taxjar.com/v2/');
    });

    it('sets custom headers via instantiation', () => {
      taxjarClient = new Taxjar({
        apiKey: 'test123',
        headers: {
          'X-TJ-Expected-Response': '422'
        }
      });
      assert.include(taxjarClient.getApiConfig('headers'), { 'X-TJ-Expected-Response': '422' });
    });

    it('sets custom headers via api config', () => {
      taxjarClient.setApiConfig('headers', { 'X-TJ-Expected-Response': '422' });
      assert.include(taxjarClient.getApiConfig('headers'), { 'X-TJ-Expected-Response': '422' });
    });

  });

  describe('categories', () => {

    it('lists tax categories', () => {
      const categoryMock = require('./mocks/categories');

      taxjarClient.categories().then(res => {
        assert(res, 'no categories');
        assert.deepEqual(res, categoryMock.CATEGORY_RES);
      });
    });

  });

  describe('rates', () => {

    it('shows tax rates for a location', () => {
      taxjarClient.ratesForLocation('90002').then(res => {
        assert(res, 'no rates');
        assert.deepEqual(res, rateMock.RATE_RES);
      });
    });

    it('shows tax rates for a location with additional params', () => {
      taxjarClient.ratesForLocation('90002', {
        city: 'Los Angeles',
        country: 'US'
      }).then(res => {
        assert(res, 'no rates');
        assert.deepEqual(res, rateMock.RATE_RES);
      });
    });

  });

  describe('taxes', () => {

    it('calculates sales tax for an order', () => {
      taxjarClient.taxForOrder({
        'from_country': 'US',
        'from_zip': '07001',
        'from_state': 'NJ',
        'to_country': 'US',
        'to_zip': '07446',
        'to_state': 'NJ',
        'amount': 16.50,
        'shipping': 1.5
      }).then(res => {
        assert.deepEqual(res, taxMock.TAX_RES);
      });
    });

  });

  describe('transactions', () => {

    it('lists order transactions', () => {
      taxjarClient.listOrders({
        'from_transaction_date': '2015/05/01',
        'to_transaction_date': '2015/05/31'
      }).then(res => {
        assert.deepEqual(res, orderMock.LIST_ORDER_RES);
      });
    });

    it('shows an order transaction', () => {
      taxjarClient.showOrder('123').then(res => {
        assert.deepEqual(res, orderMock.SHOW_ORDER_RES);
      });
    });

    it('creates an order transaction', () => {
      taxjarClient.createOrder({
        'transaction_id': '123',
        'transaction_date': '2015/05/14',
        'to_country': 'US',
        'to_zip': '90002',
        'to_state': 'CA',
        'to_city': 'Los Angeles',
        'to_street': '123 Palm Grove Ln',
        'amount': 17.45,
        'shipping': 1.5,
        'sales_tax': 0.95,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-9',
            'description': 'Fuzzy Widget',
            'unit_price': 15.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, orderMock.CREATE_ORDER_RES);
      });
    });

    it('updates an order transaction', () => {
      taxjarClient.updateOrder({
        'transaction_id': '123',
        'amount': 17.45,
        'shipping': 1.5,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-0',
            'description': 'Heavy Widget',
            'unit_price': 15.0,
            'discount': 0.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, orderMock.UPDATE_ORDER_RES);
      });
    });

    it('deletes an order transaction', () => {
      taxjarClient.deleteOrder('123').then(res => {
        assert.deepEqual(res, orderMock.DELETE_ORDER_RES);
      });
    });

    it('lists refund transactions', () => {
      taxjarClient.listRefunds({
        'from_transaction_date': '2015/05/01',
        'to_transaction_date': '2015/05/31'
      }).then(res => {
        assert.deepEqual(res, refundMock.LIST_REFUND_RES);
      });
    });

    it('shows a refund transaction', () => {
      taxjarClient.showRefund('321').then(res => {
        assert.deepEqual(res, refundMock.SHOW_REFUND_RES);
      });
    });

    it('creates a refund transaction', () => {
      taxjarClient.createRefund({
        'transaction_id': '123',
        'transaction_date': '2015/05/14',
        'transaction_reference_id': '123',
        'to_country': 'US',
        'to_zip': '90002',
        'to_state': 'CA',
        'to_city': 'Los Angeles',
        'to_street': '123 Palm Grove Ln',
        'amount': 17.45,
        'shipping': 1.5,
        'sales_tax': 0.95,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-9',
            'description': 'Fuzzy Widget',
            'unit_price': 15.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, refundMock.CREATE_REFUND_RES);
      });
    });

    it('updates a refund transaction', () => {
      taxjarClient.updateRefund({
        'transaction_id': '321',
        'amount': 17.95,
        'shipping': 2.0,
        'line_items': [
          {
            'quantity': 1,
            'product_identifier': '12-34243-0',
            'description': 'Heavy Widget',
            'unit_price': 15.0,
            'sales_tax': 0.95
          }
        ]
      }).then(res => {
        assert.deepEqual(res, refundMock.UPDATE_REFUND_RES);
      });
    });

    it('deletes a refund transaction', () => {
      taxjarClient.deleteRefund('321').then(res => {
        assert.deepEqual(res, refundMock.DELETE_REFUND_RES);
      });
    });

  });

  describe('nexus', () => {

    it('lists nexus regions', () => {
      taxjarClient.nexusRegions().then(res => {
        assert.deepEqual(res, nexusRegionMock.NEXUS_REGIONS_RES);
      });
    });

  });

  describe('validations', () => {

    it('validates a VAT number', () => {
      taxjarClient.validate({
        vat: 'FR40303265045'
      }).then(res => {
        assert.deepEqual(res, validationMock.VALIDATION_RES);
      });
    });

  });

  describe('summarized rates', () => {

    it('lists summarized rates', () => {
      taxjarClient.summaryRates().then(res => {
        assert.deepEqual(res, summaryRateMock.SUMMARY_RATES_RES);
      });
    });

  });

});
<|endoftext|>


<commit_before>var gateway = require(__dirname + '/../');
var sendPayment = require(__dirname + "/../lib//ripple/send_payment");
var buildPayment = require(__dirname + '/../lib/ripple/build_payment');
var middleware;

process.env.DATABASE_URL = gateway.config.get('DATABASE_URL');

var middlewarePath = process.env.PAYMENT_SENT_MIDDLEWARE;
if (middlewarePath) {
  middleware = require(middlewarePath);
} else {
  middleware = function(payment){
    console.log('payment sent');
    console.log(payment.to_amount, payment.to_currency);
  };
};

function loop(){
  setTimeout(function(){
    popOutgoingPayment(popOutgoingPayment);
  }, 1000);
}

function processOutgoingPayment(transaction, address, fn){

  buildPayment(transaction, address, function(err, payment) {
    if (err) { handleError(err, fn); return; }

    sendPayment(payment, function(err, resp){
      if (err || !resp.success) {
        handleError(err, fn);
      } else {
        fn(null, resp);
      }
    });
  });

  function handleError(err, fn) {
    if (err.match('No paths found')){
      fn('noPathFound', null);
      } else {
        fn('retry', null);
    }
  }
}

function popOutgoingPayment(callback) {
  gateway.api.listOutgoingPayments(function(err, transactions) {
    if (!err) {
      var transaction = transactions[0];
      if (transaction) {
        gateway.data.rippleAddresses.read(transaction.to_address_id, function(err, address) {
          processOutgoingPayment(transaction, address, function(err, resp){
            console.log(err, resp);
            if (err) {
              switch(err)
              {
              case 'retry':
                transaction.transaction_state = 'outgoing';
                break;
              case 'noPathFound':
                transaction.transaction_state = 'failed';
                break;
              default:
                transaction.transaction_state = 'failed';
              }
            } else {
              transaction.transaction_state = 'sent';
              middleware(transaction);
            }
      console.log(transaction.transaction_state);
            transaction.save().complete(function(){
        console.log(transaction.transaction_state);
        loop();
      });
          });
        });
      } else {
        loop();
      }
    } else {
      loop();
    }
  });
}

loop();

console.log('Sending outgoing ripple payments from the queue to Ripple REST.');

<commit_msg>[FEATURE] store client_resouce_id upon a sent payment<commit_after>var gateway = require(__dirname + '/../');
var sendPayment = require(__dirname + "/../lib//ripple/send_payment");
var buildPayment = require(__dirname + '/../lib/ripple/build_payment');
var middleware;

process.env.DATABASE_URL = gateway.config.get('DATABASE_URL');

var middlewarePath = process.env.PAYMENT_SENT_MIDDLEWARE;
if (middlewarePath) {
  middleware = require(middlewarePath);
} else {
  middleware = function(payment){
    console.log('payment sent');
    console.log(payment.to_amount, payment.to_currency);
  };
};

function loop(){
  setTimeout(function(){
    popOutgoingPayment(popOutgoingPayment);
  }, 1000);
}

function processOutgoingPayment(transaction, address, fn){

  buildPayment(transaction, address, function(err, payment) {
    if (err) { handleError(err, fn); return; }

    sendPayment(payment, function(err, resp){
      if (err || !resp.success) {
        handleError(err, fn);
      } else {
        fn(null, resp);
      }
    });
  });

  function handleError(err, fn) {
    if (err.match('No paths found')){
      fn('noPathFound', null);
    } else {
      fn('retry', null);
    }
  }
}

function popOutgoingPayment(callback) {
  gateway.api.listOutgoingPayments(function(err, transactions) {
    if (!err) {
      var transaction = transactions[0];
      if (transaction) {
        gateway.data.rippleAddresses.read(transaction.to_address_id, function(err, address) {
          processOutgoingPayment(transaction, address, function(err, resp){
            console.log(err, resp);
            if (err) {
              switch(err)
              {
                case 'retry':
                  transaction.transaction_state = 'outgoing';
                  break;
                case 'noPathFound':
                  transaction.transaction_state = 'failed';
                  break;
                default:
                  transaction.transaction_state = 'failed';
              }
            } else {
              transaction.transaction_state = 'sent';
              middleware(transaction);
            }
            transaction.uid = resp.client_resource_id;
            transaction.save().complete(function(){
              console.log(transaction.transaction_state);
              loop();
            });
          });
        });
      } else {
        loop();
      }
    } else {
      loop();
    }
  });
}

loop();

console.log('Sending outgoing ripple payments from the queue to Ripple REST.');

<|endoftext|>


<commit_before>var gateway = require(__dirname + '/../');
var sendPayment = require(__dirname + "/../lib//ripple/send_payment");
var buildPayment = require(__dirname + '/../lib/ripple/build_payment');
var getPaymentStatus = require(__dirname + '/../lib/ripple/get_payment_status');
var depositCallbackJob = require(__dirname+'/../lib/jobs/deposit_completion_callback.js');

console.log(depositCallbackJob.perform);

var middleware;

process.env.DATABASE_URL = gateway.config.get('DATABASE_URL');

var middlewarePath = process.env.PAYMENT_SENT_MIDDLEWARE;
if (middlewarePath) {
  middleware = require(middlewarePath);
} else {
  middleware = function(payment){
    console.log('payment sent');
    console.log(payment.to_amount, payment.to_currency);
  };
};

/**
 * @function loop
 * @description Polls database for outgoing payments.
 */
function loop(){
  setTimeout(function(){
    popOutgoingPayment();
  }, 1000);
}

/**
 * @function processOutgoingPayment
 * @description Process queued outgoing payments by calling
 * Ripple REST to construct and post a payment.
 *
 * @param transaction
 * @param address
 * @param fn
 *
 */

function processOutgoingPayment(transaction, address, fn){

  buildPayment(transaction, address, function(err, payment) {
    if (err) { handleError(err, fn); return; }
    sendPayment(payment, function(err, resp){
      if (err || !resp.success) {
        handleError(err, fn);
      } else {
        fn(null, resp);
      }
    });
  });

  function handleError(err, fn) {
    if (typeof err === 'string' && err.match('No paths found')){
      fn('noPathFound', null);
    } else {
      fn('retry', null);
    }
  }
}

/**
 *
 * @function getAndHandlePaymentStatus
 * @param statusUrl
 * @param callback
 * @param loopFunction
 *
 * @description Call this function in a loop, passing in itself as the loopFunction.
 * It will call Ripple REST for the status of a payment, and loop if
 * the state is not "validated"
 *
 */

function getAndHandlePaymentStatus(statusUrl, callback, loopFunction){
  getPaymentStatus(statusUrl, function(err, payment){
    if(err){
      callback(err, null);
      loopFunction(statusUrl, callback, loopFunction);
    } else {
      console.log('getPaymentStatus::', payment);
      if (payment.state == 'validated'){
        callback(null, payment);
      } else {
        loopFunction(statusUrl, callback, loopFunction);
      }
    }
  });
}

/**
 * @function pollPaymentStatus
 * @description Upon successfully posting a payment to Ripple REST
 * use the client_resource_id and status url provided to poll for
 * the payment's status and validate its state in the Rippled ledger.
 *
 * The pollPaymentStatus function calls a single looping function,
 * passing in the callback and looping function as parameters
 * @param {string} statusUrl
 * @param {function} callback
 * @returns {Payment}
 */
function pollPaymentStatus(statusUrl, callback){
  getAndHandlePaymentStatus(statusUrl, callback, getAndHandlePaymentStatus);
}

/**
 * @function performOutgoingPaymentJob
 *
 * Read a single ripple transaction with state of "outgoing"
 * from the database, process it by sending the payment to
 * Ripple REST. Upon response from Ripple REST record the payment
 * state in the database and poll Ripple REST for the status
 * of the payment. Finally record the transaction_state and
 * ripple transaction_hash from the ledger.
 *
 * Loops infinitely by calling itself.
 */
function popOutgoingPayment() {
  gateway.api.listOutgoingPayments(function(err, transactions) {
    if (!err) {
      var transaction = transactions[0];
      if (transaction) {
        gateway.data.rippleAddresses.read(transaction.to_address_id, function(err, address) {
          processOutgoingPayment(transaction, address, function(err, resp){
            if (err) {
              switch(err)
              {
                case 'retry':
                  transaction.state = 'outgoing';
                  break;
                case 'noPathFound':
                  transaction.state = 'failed';
                  break;
                default:
                  transaction.state = 'failed';
              }
              depositCallbackJob.perform([transaction.id], console.log);
            } else {
              var statusUrl = resp.status_url;
              transaction.state = 'sent';
              transaction.uid = resp.client_resource_id;
              transaction.save().complete(function(){
                middleware(transaction);
                pollPaymentStatus(statusUrl, function(err, payment){
                  transaction.transaction_state = payment.result;
                  transaction.transaction_hash = payment.hash;
                  switch(payment.result) {
                  case 'tesSUCCESS':
                    transaction.state = 'succeeded';
                    break;
                  default:
                    transcation.state = 'failed';
                  }
                  transaction.save().complete(function(){
                    depositCallbackJob.perform([transaction.id], console.log);
                    loop();
                  });
                });
              });
            }
          });
        });
      } else {
        loop();
      }
    } else {
      loop();
    }
  });
}

loop();

console.log('Sending outgoing ripple payments from the queue to Ripple REST.');

<commit_msg>[BUG] Loop on failed outgoing payment.<commit_after>var gateway = require(__dirname + '/../');
var sendPayment = require(__dirname + "/../lib//ripple/send_payment");
var buildPayment = require(__dirname + '/../lib/ripple/build_payment');
var getPaymentStatus = require(__dirname + '/../lib/ripple/get_payment_status');
var depositCallbackJob = require(__dirname+'/../lib/jobs/deposit_completion_callback.js');

console.log(depositCallbackJob.perform);

var middleware;

process.env.DATABASE_URL = gateway.config.get('DATABASE_URL');

var middlewarePath = process.env.PAYMENT_SENT_MIDDLEWARE;
if (middlewarePath) {
  middleware = require(middlewarePath);
} else {
  middleware = function(payment){
    console.log('payment sent');
    console.log(payment.to_amount, payment.to_currency);
  };
};

/**
 * @function loop
 * @description Polls database for outgoing payments.
 */
function loop(){
  setTimeout(function(){
    popOutgoingPayment();
  }, 1000);
}

/**
 * @function processOutgoingPayment
 * @description Process queued outgoing payments by calling
 * Ripple REST to construct and post a payment.
 *
 * @param transaction
 * @param address
 * @param fn
 *
 */

function processOutgoingPayment(transaction, address, fn){

  buildPayment(transaction, address, function(err, payment) {
    if (err) { handleError(err, fn); return; }
    sendPayment(payment, function(err, resp){
      if (err || !resp.success) {
        handleError(err, fn);
      } else {
        fn(null, resp);
      }
    });
  });

  function handleError(err, fn) {
    if (typeof err === 'string' && err.match('No paths found')){
      fn('noPathFound', null);
    } else {
      fn('retry', null);
    }
  }
}

/**
 *
 * @function getAndHandlePaymentStatus
 * @param statusUrl
 * @param callback
 * @param loopFunction
 *
 * @description Call this function in a loop, passing in itself as the loopFunction.
 * It will call Ripple REST for the status of a payment, and loop if
 * the state is not "validated"
 *
 */

function getAndHandlePaymentStatus(statusUrl, callback, loopFunction){
  getPaymentStatus(statusUrl, function(err, payment){
    if(err){
      callback(err, null);
      loopFunction(statusUrl, callback, loopFunction);
    } else {
      console.log('getPaymentStatus::', payment);
      if (payment.state == 'validated'){
        callback(null, payment);
      } else {
        loopFunction(statusUrl, callback, loopFunction);
      }
    }
  });
}

/**
 * @function pollPaymentStatus
 * @description Upon successfully posting a payment to Ripple REST
 * use the client_resource_id and status url provided to poll for
 * the payment's status and validate its state in the Rippled ledger.
 *
 * The pollPaymentStatus function calls a single looping function,
 * passing in the callback and looping function as parameters
 * @param {string} statusUrl
 * @param {function} callback
 * @returns {Payment}
 */
function pollPaymentStatus(statusUrl, callback){
  getAndHandlePaymentStatus(statusUrl, callback, getAndHandlePaymentStatus);
}

/**
 * @function performOutgoingPaymentJob
 *
 * Read a single ripple transaction with state of "outgoing"
 * from the database, process it by sending the payment to
 * Ripple REST. Upon response from Ripple REST record the payment
 * state in the database and poll Ripple REST for the status
 * of the payment. Finally record the transaction_state and
 * ripple transaction_hash from the ledger.
 *
 * Loops infinitely by calling itself.
 */
function popOutgoingPayment() {
  gateway.api.listOutgoingPayments(function(err, transactions) {
    if (!err) {
      var transaction = transactions[0];
      if (transaction) {
        gateway.data.rippleAddresses.read(transaction.to_address_id, function(err, address) {
          processOutgoingPayment(transaction, address, function(err, resp){
            if (err) {
              switch(err)
              {
                case 'retry':
                  transaction.state = 'outgoing';
                  break;
                case 'noPathFound':
                  transaction.state = 'failed';
                  break;
                default:
                  transaction.state = 'failed';
              }
              transaction.save().complete(function(){
                depositCallbackJob.perform([transaction.id], console.log);
                loop();
              });
            } else {
              var statusUrl = resp.status_url;
              transaction.state = 'sent';
              transaction.uid = resp.client_resource_id;
              transaction.save().complete(function(){
                middleware(transaction);
                pollPaymentStatus(statusUrl, function(err, payment){
                  transaction.transaction_state = payment.result;
                  transaction.transaction_hash = payment.hash;
                  switch(payment.result) {
                  case 'tesSUCCESS':
                    transaction.state = 'succeeded';
                    break;
                  default:
                    transcation.state = 'failed';
                  }
                  transaction.save().complete(function(){
                    depositCallbackJob.perform([transaction.id], console.log);
                    loop();
                  });
                });
              });
            }
          });
        });
      } else {
        loop();
      }
    } else {
      loop();
    }
  });
}

loop();

console.log('Sending outgoing ripple payments from the queue to Ripple REST.');

<|endoftext|>


<commit_before>"use strict";

var chai = require("chai");
var humanizing = require("../humanize-duration");
require("sugar");

chai.should();

describe("French humanization of duration", function() {

	beforeEach(function() {
		humanizing.language = "fr";
	});

	it("humanizes 0ms", function() {
		humanizing(0).should.equal("0");
	});

	it("humanizes French when passed as an argument", function() {
		humanizing((1).second(), "fr").should.equal("1 seconde");
		humanizing((2).seconds(), "fr").should.equal("2 secondes");
		humanizing((5).years(), "fr").should.equal("5 ans");
	});

	it("humanizes French when the default language is changed", function() {
		humanizing.language = "fr";
		humanizing((1).second()).should.equal("1 seconde");
		humanizing((2).seconds()).should.equal("2 secondes");
		humanizing((5).years()).should.equal("5 ans");
	});

});
<commit_msg>Whoops, should be resetting the language every time<commit_after>"use strict";

var chai = require("chai");
var humanizing = require("../humanize-duration");
require("sugar");

chai.should();

describe("French humanization of duration", function() {

	beforeEach(function() {
		humanizing.language = "en";
	});

	it("humanizes 0ms", function() {
		humanizing(0).should.equal("0");
	});

	it("humanizes French when passed as an argument", function() {
		humanizing((1).second(), "fr").should.equal("1 seconde");
		humanizing((2).seconds(), "fr").should.equal("2 secondes");
		humanizing((5).years(), "fr").should.equal("5 ans");
	});

	it("humanizes French when the default language is changed", function() {
		humanizing.language = "fr";
		humanizing((1).second()).should.equal("1 seconde");
		humanizing((2).seconds()).should.equal("2 secondes");
		humanizing((5).years()).should.equal("5 ans");
	});

});
<|endoftext|>


<commit_before>sys = require('sys');
assert = require('assert');
Client = require(__dirname+"/../lib/").Client;
Parser = require(__dirname+"/../lib/").Parser;
EventEmitter = require('events').EventEmitter;

assert.same = function(actual, expected) {
  for(var key in expected) {
    assert.equal(actual[key], expected[key]);
  }
};

assert.empty = function(actual) {
  assert.equal(actual.length, 0);
};

test = function(name, action) {
  for(var i = 0; i < test.tabout; i++) {
    name = ' ' + name;
  }
  test.tabout += 2;
  console.log(name);
  action();

  test.tabout -= 2;
};
test.tabout = 0;

stringToHex = function(string) {
  var b = Buffer(string,'utf8');
  var result = [];
  for(var i = 0; i < b.length; i++) {
    result.push(b[i]);
  }
  return result;
};

hexToString = function(hexArray) {
  return new Buffer(hexArray).toString('utf8');
}

BufferList = function() {
  this.buffers = [];
};

BufferList.prototype.add = function(buffer, front) {
  this.buffers[front ? "unshift" : "push"](buffer);
  return this;
};

BufferList.prototype.addInt16 = function(val, front) {
  return this.add(Buffer([(val >>> 8),(val >>> 0)]),front);
};

BufferList.prototype.getByteLength = function(initial) {
  return this.buffers.reduce(function(previous, current){
    return previous + current.length;
  },initial || 0);
};

BufferList.prototype.addInt32 = function(val, first) {
  return this.add(Buffer([
    (val >>> 24),
    (val >>> 16),
    (val >>> 8),
    (val >>> 0)
  ]),first);
};

BufferList.prototype.addCString = function(val) {
  return this.add(Buffer(val + '\0','utf8'));
};

BufferList.prototype.join = function(appendLength, char) {
  var length = this.getByteLength();
  if(appendLength) {
    this.addInt32(length+4, true);
    return this.join(false, char);
  }
  if(char) {
    this.buffers.unshift(Buffer(char,'utf8'));
    length++;
  }
  var result = Buffer(length);
  var index = 0;
  this.buffers.forEach(function(buffer) {
    buffer.copy(result, index, 0);
    index += buffer.length;
  });
  return result;
};

BufferList.concat = function() {
  var total = new BufferList();
  for(var i = 0; i < arguments.length; i++) {
    total.add(arguments[i]);
  }
  return total.join();
};

MemoryStream = function() {
  EventEmitter.call(this);
  this.packets = [];
};

sys.inherits(MemoryStream, EventEmitter);

var p = MemoryStream.prototype;
<commit_msg>added assert.length<commit_after>sys = require('sys');
assert = require('assert');
Client = require(__dirname+"/../lib/").Client;
Parser = require(__dirname+"/../lib/").Parser;
EventEmitter = require('events').EventEmitter;

assert.same = function(actual, expected) {
  for(var key in expected) {
    assert.equal(actual[key], expected[key]);
  }
};

assert.empty = function(actual) {
  assert.length(actual, 0);
};

assert.length = function(actual, expectedLength) {
  assert.equal(actual.length, expectedLength);
};

test = function(name, action) {
  for(var i = 0; i < test.tabout; i++) {
    name = ' ' + name;
  }
  test.tabout += 2;
  console.log(name);
  action();

  test.tabout -= 2;
};
test.tabout = 0;

stringToHex = function(string) {
  var b = Buffer(string,'utf8');
  var result = [];
  for(var i = 0; i < b.length; i++) {
    result.push(b[i]);
  }
  return result;
};

hexToString = function(hexArray) {
  return new Buffer(hexArray).toString('utf8');
}

BufferList = function() {
  this.buffers = [];
};

BufferList.prototype.add = function(buffer, front) {
  this.buffers[front ? "unshift" : "push"](buffer);
  return this;
};

BufferList.prototype.addInt16 = function(val, front) {
  return this.add(Buffer([(val >>> 8),(val >>> 0)]),front);
};

BufferList.prototype.getByteLength = function(initial) {
  return this.buffers.reduce(function(previous, current){
    return previous + current.length;
  },initial || 0);
};

BufferList.prototype.addInt32 = function(val, first) {
  return this.add(Buffer([
    (val >>> 24),
    (val >>> 16),
    (val >>> 8),
    (val >>> 0)
  ]),first);
};

BufferList.prototype.addCString = function(val) {
  return this.add(Buffer(val + '\0','utf8'));
};

BufferList.prototype.join = function(appendLength, char) {
  var length = this.getByteLength();
  if(appendLength) {
    this.addInt32(length+4, true);
    return this.join(false, char);
  }
  if(char) {
    this.buffers.unshift(Buffer(char,'utf8'));
    length++;
  }
  var result = Buffer(length);
  var index = 0;
  this.buffers.forEach(function(buffer) {
    buffer.copy(result, index, 0);
    index += buffer.length;
  });
  return result;
};

BufferList.concat = function() {
  var total = new BufferList();
  for(var i = 0; i < arguments.length; i++) {
    total.add(arguments[i]);
  }
  return total.join();
};

MemoryStream = function() {
  EventEmitter.call(this);
  this.packets = [];
};

sys.inherits(MemoryStream, EventEmitter);

var p = MemoryStream.prototype;
<|endoftext|>


<commit_before>module.exports = function( doxrayObject ) {

  // Returns an array of patterns with the presence of a specific property,
  // when passed a single argument.
  // Returns an array of patterns with the specific property matching a
  // specific value, when passed two arguments.
  doxrayObject.getByProperty = function( property, value ) {
    return this.patterns.filter(
      this.utils.hasProperty( property, value )
    );
  };

  doxrayObject.utils = {};

  // A function that can be passed to Array.prototype.filter().
  doxrayObject.utils.hasProperty = function( property, value ) {
    return function( pattern ) {
      if ( typeof value === 'undefined' ) {
        return pattern[ property ];
      } else {
        return pattern[ property ] && pattern[ property ].toLowerCase() === value.toLowerCase();
      }
    }
  }

  // Always return doxrayObject.
  return doxrayObject;

};
<commit_msg>Adds missing semicolon<commit_after>module.exports = function( doxrayObject ) {

  // Returns an array of patterns with the presence of a specific property,
  // when passed a single argument.
  // Returns an array of patterns with the specific property matching a
  // specific value, when passed two arguments.
  doxrayObject.getByProperty = function( property, value ) {
    return this.patterns.filter(
      this.utils.hasProperty( property, value )
    );
  };

  doxrayObject.utils = {};

  // A function that can be passed to Array.prototype.filter().
  doxrayObject.utils.hasProperty = function( property, value ) {
    return function( pattern ) {
      if ( typeof value === 'undefined' ) {
        return pattern[ property ];
      } else {
        return pattern[ property ] && pattern[ property ].toLowerCase() === value.toLowerCase();
      }
    };
  };

  // Always return doxrayObject.
  return doxrayObject;

};
<|endoftext|>


<commit_before>var phantom = require("phantom");
var when = require("when");
var cp = require("child_process");
var sys = require("sys");
var http = require("http");
var URL = require("url");

var buster = require("buster-node");
var assert = buster.assert;
var refute = buster.refute;

var ramp = require("ramp");

var phantomSharedInstance = null;

function fnReturningResolvedPromise() {
    var deferred = when.defer();
    deferred.resolve();
    return deferred.promise;
}

function killProcess(proc) {
    var deferred = when.defer();
    proc.on("exit", deferred.resolve);
    proc.kill("SIGKILL")
    return deferred.promise;
}

module.exports = {
    setUpHelpers: function (testCase, setups) {
        var deferred = when.defer();
        when.all(setups.map(function (f) { return f() })).then(function (res) {
            testCase._tearDownHelperFns = [];
            Array.prototype.slice.call(res).forEach(function (thing) {
                testCase[thing[0]] = thing[1];
                testCase._tearDownHelperFns.push(thing[2]);
            });
            deferred.resolve();
        });
        return deferred.promise;
    },

    tearDownHelpers: function (testCase) {
        return when.all(testCase._tearDownHelperFns.map(function (fn) {
            return fn();
        }))
    },

    ph: function () {
        var deferred = when.defer();
        if (phantomSharedInstance) {
            deferred.resolve(["ph", phantomSharedInstance, fnReturningResolvedPromise]);
        } else {
            console.log("Booting up Phantom.JS...");
            phantom.create(function (ph) {
                phantomSharedInstance = ph;
                deferred.resolve(["ph", phantomSharedInstance, fnReturningResolvedPromise]);
            });
        }
        return deferred.promise;
    },

    rs: function () {
        var deferred = when.defer();

        var cs = cp.spawn("node", [__dirname + "/ramp-server-loader.js"]);
        cs.stderr.pipe(process.stderr);
        cs.stdout.setEncoding("utf8");
        cs.stdout.once("data", function (data) {
            var port = parseInt(data, 10);
            var rampServerUrl = "http://localhost:" + port;

            deferred.resolve([
                "rs",
                {serverUrl: rampServerUrl,
                 captureUrl: rampServerUrl + "/capture",
                 port: port},
                function () {
                    return when.all([killProcess(cs)]);
                }]);

            cs.stdout.on("data", function (data) {
                sys.print("[SERVER PROCESS] ", data);
            });
        });

        return deferred.promise;
    },

    httpGet: function (url, cb) {
        var url = URL.parse(url);

        var body = "";

        http.request({
            hostname: url.hostname,
            port: url.port,
            path: url.path,
            method: "GET"
        }, function (res) {
            res.setEncoding("utf8");
            res.on("data", function (chunk) {
                body += chunk;
            });
            res.on("end", function () {
                cb(res, body);
            })
        }).end();
    },

    failWhenCalled: function () {
        throw new Exception("Expected not to be called.");
    },

    capture: function (test, cb) {
        test.ph.createPage(function (page) {
            page.open(test.rs.captureUrl, function (status) {
                var rc = ramp.createRampClient(test.rs.port);
                cb(rc);
            });
        });
    },

    initializeSession: function (createPromise, cb) {
        createPromise.then(
            function (sessionClientInitializer) {
                sessionClientInitializer.initialize().then(
                    function (sessionClient) { cb(sessionClient) },
                    module.exports.failWhenCalled)
            },
            module.exports.failWhenCalled);
    },

    promiseSuccess: function (promise, cb) {
        promise.then(cb, module.exports.failWhenCalled)
    },

    promiseFailure: function (promise, cb) {
        promise.then(module.exports.failWhenCalled, cb);
    }
};
<commit_msg>For good measure.<commit_after>var phantom = require("phantom");
var when = require("when");
var cp = require("child_process");
var sys = require("sys");
var http = require("http");
var URL = require("url");

var buster = require("buster-node");
var assert = buster.assert;
var refute = buster.refute;

var ramp = require("ramp");

var phantomSharedInstance = null;

function fnReturningResolvedPromise() {
    var deferred = when.defer();
    deferred.resolve();
    return deferred.promise;
}

function killProcess(proc) {
    var deferred = when.defer();
    proc.on("exit", deferred.resolve);
    proc.kill("SIGKILL")
    return deferred.promise;
}

module.exports = {
    setUpHelpers: function (testCase, setups) {
        var deferred = when.defer();
        when.all(setups.map(function (f) { return f() })).then(function (res) {
            testCase._tearDownHelperFns = [];
            Array.prototype.slice.call(res).forEach(function (thing) {
                testCase[thing[0]] = thing[1];
                testCase._tearDownHelperFns.push(thing[2]);
            });
            deferred.resolve();
        });
        return deferred.promise;
    },

    tearDownHelpers: function (testCase) {
        return when.all(testCase._tearDownHelperFns.map(function (fn) {
            return fn();
        }))
    },

    ph: function () {
        var deferred = when.defer();
        if (phantomSharedInstance) {
            deferred.resolve(["ph", phantomSharedInstance, fnReturningResolvedPromise]);
        } else {
            console.log("Booting up Phantom.JS...");
            phantom.create(function (ph) {
                console.log("Phantom.JS booted!");
                phantomSharedInstance = ph;
                deferred.resolve(["ph", phantomSharedInstance, fnReturningResolvedPromise]);
            });
        }
        return deferred.promise;
    },

    rs: function () {
        var deferred = when.defer();

        var cs = cp.spawn("node", [__dirname + "/ramp-server-loader.js"]);
        cs.stderr.pipe(process.stderr);
        cs.stdout.setEncoding("utf8");
        cs.stdout.once("data", function (data) {
            var port = parseInt(data, 10);
            var rampServerUrl = "http://localhost:" + port;

            deferred.resolve([
                "rs",
                {serverUrl: rampServerUrl,
                 captureUrl: rampServerUrl + "/capture",
                 port: port},
                function () {
                    return when.all([killProcess(cs)]);
                }]);

            cs.stdout.on("data", function (data) {
                sys.print("[SERVER PROCESS] ", data);
            });
        });

        return deferred.promise;
    },

    httpGet: function (url, cb) {
        var url = URL.parse(url);

        var body = "";

        http.request({
            hostname: url.hostname,
            port: url.port,
            path: url.path,
            method: "GET"
        }, function (res) {
            res.setEncoding("utf8");
            res.on("data", function (chunk) {
                body += chunk;
            });
            res.on("end", function () {
                cb(res, body);
            })
        }).end();
    },

    failWhenCalled: function () {
        throw new Exception("Expected not to be called.");
    },

    capture: function (test, cb) {
        test.ph.createPage(function (page) {
            page.open(test.rs.captureUrl, function (status) {
                var rc = ramp.createRampClient(test.rs.port);
                cb(rc);
            });
        });
    },

    initializeSession: function (createPromise, cb) {
        createPromise.then(
            function (sessionClientInitializer) {
                sessionClientInitializer.initialize().then(
                    function (sessionClient) { cb(sessionClient) },
                    module.exports.failWhenCalled)
            },
            module.exports.failWhenCalled);
    },

    promiseSuccess: function (promise, cb) {
        promise.then(cb, module.exports.failWhenCalled)
    },

    promiseFailure: function (promise, cb) {
        promise.then(module.exports.failWhenCalled, cb);
    }
};
<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */

var fixtures = require('./fixtures/json')
  , Merger = require('../lib/merger').Merger;

module.exports = function() {
  describe('Merger', function() {
    describe('#constructor', function() {
      it('should set counter to 0', function() {
        var merger = new Merger();
        expect(merger.counter).to.equal(0);
      });
    });

    describe('#mergeTimeStamp', function() {
      it('should use old timestamp', function() {
        var merger = new Merger();
        var time = 1000;
        var res = merger.mergeTimeStamp({ 'test' : {}}, { 'test' : { timestamp : time }}, 'test');
        expect(res.test.timestamp).to.equal(time);
      });

      it('should use current timstamp as timestamp whenever old timestamp is undefined', function() {
        var merger = new Merger();
        var res = merger.mergeTimeStamp({ 'test' : {}}, { 'test' : {}}, 'test');
        expect(res.test.timestamp).to.be.a('number');
      });
    });
  });
};
<commit_msg>Added description placeholder for testing<commit_after>
/**
 * Module dependencies
 */

var fixtures = require('./fixtures/json')
  , Merger = require('../lib/merger').Merger;

module.exports = function() {
  describe('Merger', function() {
    describe('#constructor', function() {
      it('should set counter to 0', function() {
        var merger = new Merger();
        expect(merger.counter).to.equal(0);
      });
    });

    describe('#mergeTimeStamp', function() {
      it('should use old timestamp', function() {
        var merger = new Merger();
        var time = 1000;
        var res = merger.mergeTimeStamp({ 'test' : {}}, { 'test' : { timestamp : time }}, 'test');
        expect(res.test.timestamp).to.equal(time);
      });

      it('should use current timstamp as timestamp whenever old timestamp is undefined', function() {
        var merger = new Merger();
        var res = merger.mergeTimeStamp({ 'test' : {}}, { 'test' : {}}, 'test');
        expect(res.test.timestamp).to.be.a('number');
      });
    });

    describe('#mergeId', function() {

    });

    describe('#mergeTranslations', function() {

    });
  });
};
<|endoftext|>