<commit_before>angular.module('anol.featurepopup')
/**
 * @ngdoc directive
 * @name anol.featurepopup.directive:anolFeaturePopup
 *
 * @restrict A
 *
 * @param {string} templateUrl Url to template to use instead of default one
 * @param {anol.layer.Feature} layers Layers to show popup for
 * @param {number} tolerance Click tolerance in pixel
 * @param {object} openFor Accepts an object with layer and feature property. If changed, a popup is shown for given value
 *
 * @description
 * Shows a popup for selected feature
 */
.directive('anolFeaturePopup', ['$timeout', 'MapService', 'LayersService', 'ControlsService', function($timeout, MapService, LayersService, ControlsService) {
    return {
        restrict: 'A',
        scope: {
            'layers': '=',
            'tolerance': '=',
            'openFor': '='
        },
        replace: true,
        transclude: true,
        templateUrl: function(tElement, tAttrs) {
            var defaultUrl = 'src/modules/featurepopup/templates/popup.html';
            return tAttrs.templateUrl || defaultUrl;
        },
        link: function(scope, element, attrs) {
            // TODO find solution for multiple directives in one page
            var multiselect = angular.isDefined(attrs.multiselect);
            var clickPointSelect = angular.isDefined(attrs.clickPointSelect);
            scope.map = MapService.getMap();
            scope.popupVisible = false;

            scope.feature = undefined;
            scope.layer = undefined;
            scope.selectes = {};

            if(angular.isUndefined(scope.layers)) {
                scope.layers = [];
                scope.$watch(function() {
                    return LayersService.flattedLayers;
                }, function(layers) {
                    scope.layers.length = 0;
                    angular.forEach(layers, function(layer) {
                        if(!(layer instanceof anol.layer.Feature)) {
                            return;
                        }
                        scope.layers.push(layer);
                    });
                });
            }

            scope.overlayOptions = {
                element: element[0],
                autoPan: true,
                autoPanAnimation: {
                    duration: 250
                }

            };

            scope.popup = new ol.Overlay(scope.overlayOptions);
            scope.map.addOverlay(scope.popup);

            var selectInteraction;
            var interactions = [];

            var handleMultiSelect = function(evt) {
                scope.selects = {};
                if(evt.selected.length === 0) {
                    return false;
                }
                angular.forEach(evt.selected, function(feature) {
                    var layer = selectInteraction.getLayer(feature).get('anolLayer');
                    if(scope.selects[layer.name] === undefined) {
                        scope.selects[layer.name] = {
                            layer: layer,
                            features: [feature]
                        };
                    } else {
                        scope.selects[layer.name].features.push(feature);
                    }
                });
                return true;
            };
            var handleSingleSelect = function(evt) {
                scope.feature = undefined;
                scope.layer = undefined;
                if(evt.selected.length === 0) {
                    return false;
                }
                scope.feature = evt.selected[0];
                scope.layer = selectInteraction.getLayer(scope.feature).get('anolLayer');
                return true;
            };

            var handleSelect = function(evt) {
                scope.popupVisible = false;
                scope.$digest();
                var _handleSelect = multiselect === true ? handleMultiSelect : handleSingleSelect;

                if(_handleSelect(evt) === true) {
                    scope.popupVisible = true;
                    scope.$digest();
                    // wait until scope changes applied ($digest cycle completed) before set popup position
                    // otherwise Overlay.autoPan is not work correctly
                    $timeout(function() {
                        // popup could have been closed by $scope.close()
                        if(scope.popupVisible === true) {
                            scope.popup.setPosition(evt.mapBrowserEvent.coordinate);
                        }
                    });
                }
            };

            var handleClick = function(evt) {
                scope.popupVisible = false;
                scope.$digest();
                var features = [];
                var extent = [
                    evt.coordinate[0] - (scope.tolerance || 0),
                    evt.coordinate[1] - (scope.tolerance || 0),
                    evt.coordinate[0] + (scope.tolerance || 0),
                    evt.coordinate[1] + (scope.tolerance || 0)
                ];
                scope.selects = {};
                var found = false;
                angular.forEach(scope.layers, function(layer) {
                    var _features = layer.olLayer.getSource().getFeaturesInExtent(extent);
                    if(_features.length > 0) {
                        found = true;
                        scope.selects[layer.name] = {
                            layer: layer,
                            features: _features
                        };
                    }
                });
                if(found === true) {
                    scope.popupVisible = true;
                    scope.$digest();
                    // wait until scope changes applied ($digest cycle completed) before set popup position
                    // otherwise Overlay.autoPan is not work correctly
                    $timeout(function() {
                        // popup could have been closed by $scope.close()
                        if(scope.popupVisible === true) {
                            scope.popup.setPosition(evt.coordinate);
                        }
                    });
                }
            };

            var recreateInteractions = function() {
                scope.popupVisible = false;

                angular.forEach(interactions, function(interaction) {
                    interaction.setActive(false);
                    scope.map.removeInteraction(interaction);
                });

                var olLayers = [];
                var snapInteractions = [];
                angular.forEach(scope.layers, function(layer) {
                    olLayers.push(layer.olLayer);
                    snapInteractions.push(new ol.interaction.Snap({
                        source: layer.olLayer.getSource(),
                        pixelTolerance: scope.tolerance
                    }));
                });
                selectInteraction = new ol.interaction.Select({
                    toggleCondition: ol.events.condition.never,
                    multi: multiselect,
                    layers: olLayers,
                    style: function(feature) {
                        // prevents changing feature style
                        // TODO find better solution
                        var style = feature.getStyle() || selectInteraction.getLayer(feature).getStyle();
                        if(angular.isFunction(style)) {
                            return style(feature);
                        }
                        return style;
                    }
                });
                selectInteraction.on('select', handleSelect);

                interactions = [selectInteraction].concat(snapInteractions);
                angular.forEach(interactions, function(interaction) {
                    scope.map.addInteraction(interaction);
                });
            };

            var changeCursor = function(evt) {
                var pixel = scope.map.getEventPixel(evt.originalEvent);

                var hit = scope.map.hasFeatureAtPixel(pixel, function(layer) {
                    return scope.layers.indexOf(layer.get('anolLayer')) !== -1;
                });

                scope.map.getTarget().style.cursor = hit ? 'pointer' : '';
            };
            var changeCursorEvtKey;

            var bindCursorChange = function() {
                if((scope.layers === undefined || scope.layers.length === 0) && changeCursorEvtKey !== undefined) {
                    scope.map.unByKey(changeCursorEvtKey);
                } else if(scope.layers !== undefined && scope.layers.length !== 0) {
                    changeCursorEvtKey = scope.map.on('pointermove', changeCursor);
                }
            };

            var control = new anol.control.Control({
                subordinate: true,
                olControl: null
            });
            control.onDeactivate(function() {
                angular.forEach(interactions, function(interaction) {
                    interaction.setActive(false);
                });
                scope.map.unByKey(changeCursorEvtKey);
                scope.map.getTarget().style.cursor = '';
            });
            control.onActivate(function() {
                angular.forEach(interactions, function(interaction) {
                    interaction.setActive(true);
                });
                changeCursorEvtKey = scope.map.on('pointermove', changeCursor);
            });

            if(clickPointSelect === true) {
                scope.map.on('singleclick', handleClick, this);
            } else {
                recreateInteractions();
                scope.$watch('layers', recreateInteractions);
            }

            control.activate();

            ControlsService.addControl(control);

            scope.$watch('layers', bindCursorChange);
            scope.$watch('popupVisible', function(visible) {
                if(!visible) {
                    if(selectInteraction !== undefined) {
                        selectInteraction.getFeatures().clear();
                    }
                    scope.layer = undefined;
                    scope.feature = undefined;
                }
            });
            scope.$watch('openFor', function(openFor) {
                if(angular.isDefined(openFor)) {
                    scope.popupVisible = false;
                    // wait until digest cycle complete
                    $timeout(function() {
                        scope.layer = openFor.layer;
                        scope.feature = openFor.feature;
                        scope.openFor = undefined;
                        scope.popupVisible = true;
                        $timeout(function() {
                            scope.popup.setPosition(scope.feature.getGeometry().getLastCoordinate());
                        });
                    });
                }
            });
        },
        controller: function($scope, $element, $attrs) {
            this.close = function() {
                $scope.popupVisible = false;
            };
            $scope.close = this.close;
        }
    };
}]);
<commit_msg>Refactored set popup position<commit_after>angular.module('anol.featurepopup')
/**
 * @ngdoc directive
 * @name anol.featurepopup.directive:anolFeaturePopup
 *
 * @restrict A
 *
 * @param {string} templateUrl Url to template to use instead of default one
 * @param {anol.layer.Feature} layers Layers to show popup for
 * @param {number} tolerance Click tolerance in pixel
 * @param {object} openFor Accepts an object with layer and feature property. If changed, a popup is shown for given value
 *
 * @description
 * Shows a popup for selected feature
 */
.directive('anolFeaturePopup', ['$timeout', 'MapService', 'LayersService', 'ControlsService', function($timeout, MapService, LayersService, ControlsService) {
    return {
        restrict: 'A',
        scope: {
            'layers': '=',
            'tolerance': '=',
            'openFor': '='
        },
        replace: true,
        transclude: true,
        templateUrl: function(tElement, tAttrs) {
            var defaultUrl = 'src/modules/featurepopup/templates/popup.html';
            return tAttrs.templateUrl || defaultUrl;
        },
        link: function(scope, element, attrs) {
            // TODO find solution for multiple directives in one page
            var multiselect = angular.isDefined(attrs.multiselect);
            var clickPointSelect = angular.isDefined(attrs.clickPointSelect);
            scope.map = MapService.getMap();
            scope.popupVisible = false;

            scope.feature = undefined;
            scope.layer = undefined;
            scope.selectes = {};

            if(angular.isUndefined(scope.layers)) {
                scope.layers = [];
                scope.$watch(function() {
                    return LayersService.flattedLayers;
                }, function(layers) {
                    scope.layers.length = 0;
                    angular.forEach(layers, function(layer) {
                        if(!(layer instanceof anol.layer.Feature)) {
                            return;
                        }
                        scope.layers.push(layer);
                    });
                });
            }

            scope.overlayOptions = {
                element: element[0],
                autoPan: true,
                autoPanAnimation: {
                    duration: 250
                }

            };

            scope.popup = new ol.Overlay(scope.overlayOptions);
            scope.map.addOverlay(scope.popup);

            var selectInteraction;
            var interactions = [];

            var handleMultiSelect = function(evt) {
                scope.selects = {};
                if(evt.selected.length === 0) {
                    return false;
                }
                angular.forEach(evt.selected, function(feature) {
                    var layer = selectInteraction.getLayer(feature).get('anolLayer');
                    if(scope.selects[layer.name] === undefined) {
                        scope.selects[layer.name] = {
                            layer: layer,
                            features: [feature]
                        };
                    } else {
                        scope.selects[layer.name].features.push(feature);
                    }
                });
                return true;
            };
            var handleSingleSelect = function(evt) {
                scope.feature = undefined;
                scope.layer = undefined;
                if(evt.selected.length === 0) {
                    return false;
                }
                scope.feature = evt.selected[0];
                scope.layer = selectInteraction.getLayer(scope.feature).get('anolLayer');
                return true;
            };

            var handleSelect = function(evt) {
                scope.popupVisible = false;
                scope.$digest();
                var _handleSelect = multiselect === true ? handleMultiSelect : handleSingleSelect;

                if(_handleSelect(evt) === true) {
                    scope.coordinate = evt.mapBrowserEvent.coordinate;
                    scope.popupVisible = true;
                    scope.$digest();
                }
            };

            var handleClick = function(evt) {
                scope.popupVisible = false;
                scope.$digest();
                var features = [];
                var extent = [
                    evt.coordinate[0] - (scope.tolerance || 0),
                    evt.coordinate[1] - (scope.tolerance || 0),
                    evt.coordinate[0] + (scope.tolerance || 0),
                    evt.coordinate[1] + (scope.tolerance || 0)
                ];
                scope.selects = {};
                var found = false;
                angular.forEach(scope.layers, function(layer) {
                    var _features = layer.olLayer.getSource().getFeaturesInExtent(extent);
                    if(_features.length > 0) {
                        found = true;
                        scope.selects[layer.name] = {
                            layer: layer,
                            features: _features
                        };
                    }
                });
                if(found === true) {
                    scope.coordinate = evt.coordinate;
                    scope.popupVisible = true;
                    scope.$digest();
                }
            };

            var recreateInteractions = function() {
                scope.popupVisible = false;

                angular.forEach(interactions, function(interaction) {
                    interaction.setActive(false);
                    scope.map.removeInteraction(interaction);
                });

                var olLayers = [];
                var snapInteractions = [];
                angular.forEach(scope.layers, function(layer) {
                    olLayers.push(layer.olLayer);
                    snapInteractions.push(new ol.interaction.Snap({
                        source: layer.olLayer.getSource(),
                        pixelTolerance: scope.tolerance
                    }));
                });
                selectInteraction = new ol.interaction.Select({
                    toggleCondition: ol.events.condition.never,
                    multi: multiselect,
                    layers: olLayers,
                    style: function(feature) {
                        // prevents changing feature style
                        // TODO find better solution
                        var style = feature.getStyle() || selectInteraction.getLayer(feature).getStyle();
                        if(angular.isFunction(style)) {
                            return style(feature);
                        }
                        return style;
                    }
                });
                selectInteraction.on('select', handleSelect);

                interactions = [selectInteraction].concat(snapInteractions);
                angular.forEach(interactions, function(interaction) {
                    scope.map.addInteraction(interaction);
                });
            };

            var changeCursor = function(evt) {
                var pixel = scope.map.getEventPixel(evt.originalEvent);

                var hit = scope.map.hasFeatureAtPixel(pixel, function(layer) {
                    return scope.layers.indexOf(layer.get('anolLayer')) !== -1;
                });

                scope.map.getTarget().style.cursor = hit ? 'pointer' : '';
            };
            var changeCursorEvtKey;

            var bindCursorChange = function() {
                if((scope.layers === undefined || scope.layers.length === 0) && changeCursorEvtKey !== undefined) {
                    scope.map.unByKey(changeCursorEvtKey);
                } else if(scope.layers !== undefined && scope.layers.length !== 0) {
                    changeCursorEvtKey = scope.map.on('pointermove', changeCursor);
                }
            };

            var control = new anol.control.Control({
                subordinate: true,
                olControl: null
            });
            control.onDeactivate(function() {
                angular.forEach(interactions, function(interaction) {
                    interaction.setActive(false);
                });
                scope.map.unByKey(changeCursorEvtKey);
                scope.map.getTarget().style.cursor = '';
            });
            control.onActivate(function() {
                angular.forEach(interactions, function(interaction) {
                    interaction.setActive(true);
                });
                changeCursorEvtKey = scope.map.on('pointermove', changeCursor);
            });

            if(clickPointSelect === true) {
                scope.map.on('singleclick', handleClick, this);
            } else {
                recreateInteractions();
                scope.$watch('layers', recreateInteractions);
            }

            control.activate();

            ControlsService.addControl(control);

            scope.$watch('layers', bindCursorChange);
            scope.$watch('popupVisible', function(visible) {
                if(visible === false) {
                    if(selectInteraction !== undefined) {
                        selectInteraction.getFeatures().clear();
                    }
                    scope.layer = undefined;
                    scope.feature = undefined;
                    scope.coordinate = undefined;
                } else if(scope.coordinate !== undefined) {
                    // wait until scope changes applied ($digest cycle completed) before set popup position
                    // otherwise Overlay.autoPan is not work correctly
                    $timeout(function() {
                        scope.popup.setPosition(scope.coordinate);
                    });
                }
            });
            scope.$watch('openFor', function(openFor) {
                if(angular.isDefined(openFor)) {
                    scope.popupVisible = false;
                    // wait until digest cycle complete
                    $timeout(function() {
                        scope.layer = openFor.layer;
                        scope.feature = openFor.feature;
                        scope.openFor = undefined;
                        scope.coordinate = scope.feature.getGeometry().getLastCoordinate();
                        scope.popupVisible = true;
                    });
                }
            });
        },
        controller: function($scope, $element, $attrs) {
            this.close = function() {
                $scope.popupVisible = false;
            };
            $scope.close = this.close;
        }
    };
}]);
<|endoftext|>


<commit_before>/**
 * @fileOverview Command Line Handlers Helper for Firefox 3.5 or later
 * @author       ClearCode Inc.
 * @version      5
 *
 * @license
 *   The MIT License, Copyright (c) 2016 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-command-line-handler
 */

if (typeof window == 'undefined' ||
	(window && typeof window.constructor == 'function'))
	this.EXPORTED_SYMBOLS = ['CLHHelper'];

// var namespace;
if (typeof namespace == 'undefined') {
	// If namespace.jsm is available, export symbols to the shared namespace.
	// See: https://github.com/piroor/fxaddonlibs/blob/master/namespace.jsm
	try {
		let ns = {};
		Components.utils.import('resource://my-modules/namespace.jsm', ns);
		namespace = ns.getNamespaceFor('clear-code.com');
	}
	catch(e) {
		namespace = (typeof window != 'undefined' ? window : null ) || {};
	}
}

var CLHHelper;
(function() {
	const currentRevision = 5;

	var loadedRevision = 'CLHHelper' in namespace ?
			namespace.CLHHelper.revision :
			0 ;
	if (loadedRevision && loadedRevision > currentRevision) {
		CLHHelper = namespace.CLHHelper;
		return;
	}

	const Cc = Components.classes;
	const Ci = Components.interfaces;

	/**
	 * @class The helper service, provides features to implement nsICommandHandler components.
	 */
	CLHHelper = {
		/** @private */
		revision : currentRevision,

		/** @private */
		_getValue : function(aOption, aCommandLine, aDefaultValue)
		{
			if (aDefaultValue === void(0)) aDefaultValue = '';
			try {
				var value = aCommandLine.handleFlagWithParam(aOption.replace(/^-/, ''), false);
				return value === null ? aDefaultValue : value ;
			}
			catch(e) {
			}
			return aDefaultValue;
		},

		/**
		 * Checks whether the command line option is given or not.
		 *
		 * @example
		 *   // > firefox.exe -my-option
		 *   CLHHelper.getBooleanValue('my-option', aCommandLine);
		 *   // => true
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 *
		 * @returns {boolean}
		 *   If the option is given, true will be returned.
		 *   Otherwise false.
		 */
		getBooleanValue : function(aOption, aCommandLine)
		{
			try {
				if (aCommandLine.handleFlag(aOption.replace(/^-/, ''), false))
					return true;
			}
			catch(e) {
			}
			return false;
		},

		/**
		 * Gets the value of the command line option as a number.
		 *
		 * @example
		 *   // > firefox.exe -my-option "200"
		 *   CLHHelper.getNumericValue('my-option', aCommandLine);
		 *   // => 200
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 * @param {number=} aDefaultValue (optional)
		 *   The default value which is used when the option is not given.
		 *   This is 0 by default.
		 *
		 * @returns {number}
		 *   A numeric value specified by the option. If the option is not
		 *   given, or it is not a number, then this returns the aDefaultValue
		 *   as is.
		 */
		getNumericValue : function(aOption, aCommandLine, aDefaultValue)
		{
			if (!aDefaultValue) aDefaultValue = 0;
			var value = this._getValue(aOption, aCommandLine, aDefaultValue);
			if (!value) return aDefaultValue;
			value = Number(value);
			return isNaN(value) ? aDefaultValue : value ;
		},

		/**
		 * Gets the value of the command line option as a string.
		 *
		 * @example
		 *   // > firefox.exe -my-option "my-value"
		 *   CLHHelper.getStringValue('my-option', aCommandLine);
		 *   // => "my-value"
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 * @param {string=} aDefaultValue (optional)
		 *   The default value which is used when the option is not given.
		 *   This is "" (blank string) by default.
		 *
		 * @returns {string}
		 *   A string value specified by the option. If the option is not
		 *   given, then this returns the aDefaultValue as is.
		 */
		getStringValue : function(aOption, aCommandLine, aDefaultValue)
		{
			return this._getValue(aOption, aCommandLine, aDefaultValue);
		},

		/**
		 * Gets a full path or an absolute file URL from a related path
		 * string specified by a command line option.
		 *
		 * @example
		 *   // > firefox.exe -my-option "../../file.txt"
		 *   CLHHelper.getFullPath('my-option', aCommandLine);
		 *   // => "file:///C:/.../file.txt"
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 * @param {string=} aDefaultValue (optional)
		 *   The default value which is used when the option is not given.
		 *   This is "" (blank string) by default.
		 *
		 * @returns {string}
		 *   A full path or an absolute file URL. If the option is not
		 *   given, then this returns the aDefaultValue as is.
		 */
		getFullPath : function(aOption, aCommandLine, aDefaultValue)
		{
			if (!aDefaultValue) aDefaultValue = '';
			var value = this._getValue(aOption, aCommandLine, aDefaultValue);
			if (!value) return null;
			if (value.indexOf('/') < 0) {
				// we cannot use resolveFile for missing file...
				let file = aCommandLine.resolveFile('');
				file.append(value);
				file.normalize();
				return file.path;
			}
			else {
				value = aCommandLine.resolveURI(value);
				return value.spec;
			}
		},

		/**
		 * Creates a formatted text for nsICommandLineHandler::helpInfo, from
		 * a hash.
		 *
		 * @example
		 *   MyCommandLineHandler.prototype.__defineGetter__(
		 *     'helpInfo',
		 *     function() {
		 *       return CLHHelper.formatHelpInfo({
		 *         '-boolean-option'     : 'It is a boolean option.',
		 *         '-path-option <path>' : 'It is a path option.'
		 *       });
		 *     }
		 *  );
		 *
		 * @param {Object.<string, string>} aDescriptions
		 *   A hash which have items for each command line option: the key is
		 *   the name of the option, and the value is the description.
		 *
		 * @returns {string}
		 *   An UTF-8 string which is formatted by: wrapped at 76 columns, and
		 *   they have 23 cols for the name, rest for the description.
		 */
		formatHelpInfo : function(aDescriptions)
		{
			var lines = [];
			var indent = '                       ';
			for (var i in aDescriptions)
			{
				let option = (i.indexOf('-') == 0) ? i : '-' + i ;
				let description = aDescriptions[i].replace(/^\s+|\s+$/g, '');

				option = '  '+option;
				if (option.length > 22) {
					lines.push(option);
					description = indent + description;
				}
				else {
					while (option.length < 23)
					{
						option += ' ';
					}
					description = option + description;
				}

				while (true)
				{
					lines.push(description.substring(0, 75).replace(/\s+$/, ''));
					if (description.length < 75)
						break;
					description = indent + description.substring(75).replace(/^\s+/, '');
				}
			}
			return this._UCS2ToUTF8(lines.join('\n'))+'\n';
		},

		/**
		 * @private
		 * Converts an Unicode string (UCS-2) to an UTF-8 string.
		 *
		 * @param {string} aInput
		 *   The input string.
		 *
		 * @returns {string}
		 *   The UTF-8 converted version of the given string.
		 */
		_UCS2ToUTF8 : function(aInput)
		{
			return unescape(encodeURIComponent(aInput));
		}
	};

	namespace.CLHHelper = CLHHelper;
})();
<commit_msg>Ignore error on normalization.<commit_after>/**
 * @fileOverview Command Line Handlers Helper for Firefox 3.5 or later
 * @author       ClearCode Inc.
 * @version      5
 *
 * @license
 *   The MIT License, Copyright (c) 2016 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-command-line-handler
 */

if (typeof window == 'undefined' ||
	(window && typeof window.constructor == 'function'))
	this.EXPORTED_SYMBOLS = ['CLHHelper'];

// var namespace;
if (typeof namespace == 'undefined') {
	// If namespace.jsm is available, export symbols to the shared namespace.
	// See: https://github.com/piroor/fxaddonlibs/blob/master/namespace.jsm
	try {
		let ns = {};
		Components.utils.import('resource://my-modules/namespace.jsm', ns);
		namespace = ns.getNamespaceFor('clear-code.com');
	}
	catch(e) {
		namespace = (typeof window != 'undefined' ? window : null ) || {};
	}
}

var CLHHelper;
(function() {
	const currentRevision = 5;

	var loadedRevision = 'CLHHelper' in namespace ?
			namespace.CLHHelper.revision :
			0 ;
	if (loadedRevision && loadedRevision > currentRevision) {
		CLHHelper = namespace.CLHHelper;
		return;
	}

	const Cc = Components.classes;
	const Ci = Components.interfaces;

	/**
	 * @class The helper service, provides features to implement nsICommandHandler components.
	 */
	CLHHelper = {
		/** @private */
		revision : currentRevision,

		/** @private */
		_getValue : function(aOption, aCommandLine, aDefaultValue)
		{
			if (aDefaultValue === void(0)) aDefaultValue = '';
			try {
				var value = aCommandLine.handleFlagWithParam(aOption.replace(/^-/, ''), false);
				return value === null ? aDefaultValue : value ;
			}
			catch(e) {
			}
			return aDefaultValue;
		},

		/**
		 * Checks whether the command line option is given or not.
		 *
		 * @example
		 *   // > firefox.exe -my-option
		 *   CLHHelper.getBooleanValue('my-option', aCommandLine);
		 *   // => true
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 *
		 * @returns {boolean}
		 *   If the option is given, true will be returned.
		 *   Otherwise false.
		 */
		getBooleanValue : function(aOption, aCommandLine)
		{
			try {
				if (aCommandLine.handleFlag(aOption.replace(/^-/, ''), false))
					return true;
			}
			catch(e) {
			}
			return false;
		},

		/**
		 * Gets the value of the command line option as a number.
		 *
		 * @example
		 *   // > firefox.exe -my-option "200"
		 *   CLHHelper.getNumericValue('my-option', aCommandLine);
		 *   // => 200
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 * @param {number=} aDefaultValue (optional)
		 *   The default value which is used when the option is not given.
		 *   This is 0 by default.
		 *
		 * @returns {number}
		 *   A numeric value specified by the option. If the option is not
		 *   given, or it is not a number, then this returns the aDefaultValue
		 *   as is.
		 */
		getNumericValue : function(aOption, aCommandLine, aDefaultValue)
		{
			if (!aDefaultValue) aDefaultValue = 0;
			var value = this._getValue(aOption, aCommandLine, aDefaultValue);
			if (!value) return aDefaultValue;
			value = Number(value);
			return isNaN(value) ? aDefaultValue : value ;
		},

		/**
		 * Gets the value of the command line option as a string.
		 *
		 * @example
		 *   // > firefox.exe -my-option "my-value"
		 *   CLHHelper.getStringValue('my-option', aCommandLine);
		 *   // => "my-value"
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 * @param {string=} aDefaultValue (optional)
		 *   The default value which is used when the option is not given.
		 *   This is "" (blank string) by default.
		 *
		 * @returns {string}
		 *   A string value specified by the option. If the option is not
		 *   given, then this returns the aDefaultValue as is.
		 */
		getStringValue : function(aOption, aCommandLine, aDefaultValue)
		{
			return this._getValue(aOption, aCommandLine, aDefaultValue);
		},

		/**
		 * Gets a full path or an absolute file URL from a related path
		 * string specified by a command line option.
		 *
		 * @example
		 *   // > firefox.exe -my-option "../../file.txt"
		 *   CLHHelper.getFullPath('my-option', aCommandLine);
		 *   // => "file:///C:/.../file.txt"
		 *
		 * @param {string} aOption
		 *   The name of the option.
		 * @param {nsICommandLine} aCommandLine
		 *   The command line object given to nsICommandLineHandler::handle.
		 * @param {string=} aDefaultValue (optional)
		 *   The default value which is used when the option is not given.
		 *   This is "" (blank string) by default.
		 *
		 * @returns {string}
		 *   A full path or an absolute file URL. If the option is not
		 *   given, then this returns the aDefaultValue as is.
		 */
		getFullPath : function(aOption, aCommandLine, aDefaultValue)
		{
			if (!aDefaultValue) aDefaultValue = '';
			var value = this._getValue(aOption, aCommandLine, aDefaultValue);
			if (!value) return null;
			if (value.indexOf('/') < 0) {
				// we cannot use resolveFile for missing file...
				let file = aCommandLine.resolveFile('');
				file.append(value);
				try{
					file.normalize();
				}
				catch(e){
				}
				return file.path;
			}
			else {
				value = aCommandLine.resolveURI(value);
				return value.spec;
			}
		},

		/**
		 * Creates a formatted text for nsICommandLineHandler::helpInfo, from
		 * a hash.
		 *
		 * @example
		 *   MyCommandLineHandler.prototype.__defineGetter__(
		 *     'helpInfo',
		 *     function() {
		 *       return CLHHelper.formatHelpInfo({
		 *         '-boolean-option'     : 'It is a boolean option.',
		 *         '-path-option <path>' : 'It is a path option.'
		 *       });
		 *     }
		 *  );
		 *
		 * @param {Object.<string, string>} aDescriptions
		 *   A hash which have items for each command line option: the key is
		 *   the name of the option, and the value is the description.
		 *
		 * @returns {string}
		 *   An UTF-8 string which is formatted by: wrapped at 76 columns, and
		 *   they have 23 cols for the name, rest for the description.
		 */
		formatHelpInfo : function(aDescriptions)
		{
			var lines = [];
			var indent = '                       ';
			for (var i in aDescriptions)
			{
				let option = (i.indexOf('-') == 0) ? i : '-' + i ;
				let description = aDescriptions[i].replace(/^\s+|\s+$/g, '');

				option = '  '+option;
				if (option.length > 22) {
					lines.push(option);
					description = indent + description;
				}
				else {
					while (option.length < 23)
					{
						option += ' ';
					}
					description = option + description;
				}

				while (true)
				{
					lines.push(description.substring(0, 75).replace(/\s+$/, ''));
					if (description.length < 75)
						break;
					description = indent + description.substring(75).replace(/^\s+/, '');
				}
			}
			return this._UCS2ToUTF8(lines.join('\n'))+'\n';
		},

		/**
		 * @private
		 * Converts an Unicode string (UCS-2) to an UTF-8 string.
		 *
		 * @param {string} aInput
		 *   The input string.
		 *
		 * @returns {string}
		 *   The UTF-8 converted version of the given string.
		 */
		_UCS2ToUTF8 : function(aInput)
		{
			return unescape(encodeURIComponent(aInput));
		}
	};

	namespace.CLHHelper = CLHHelper;
})();
<|endoftext|>


<commit_before>var rethinkdbdash = require('rethinkdbdash');
var Model = require(__dirname+'/model.js');

/*
 * Main method
 * Create the default database
 *
 * Fields in `options` can be:
 *  - `min`: <number> - minimum number of connections in the pool, default 50
 *  - `max`: <number> -  maximum number of connections in the pool, default 1000
 *  - `bufferSize`: <number> - minimum number of connections available in the pool, default 50
 *  - `timeoutError`: <number> - wait time before reconnecting in case of an error (in ms), default 1000
 *  - `timeoutGb`: <number> - how long the pool keep a connection that hasn't been used (in ms), default 60*60*1000
 *  - `enforce`: {missing: <boolean>, extra: <boolean>, type: <boolean>}
 *  - `timeFormat`: "raw" or "native"
 *
 */
function Thinky(config) {
    var self = this;

    config = config || {};
    config.db = config.db || 'test'; // We need the default db to create it.

    this._options = {}
    this._options.enforce_missing = (config.enforce_missing != null) ? config.enforce_missing : false;
    this._options.enforce_extra = (config.enforce_extra != null) ? config.enforce_extra : false;
    this._options.enforce_type = (config.enforce_type != null) ? config.enforce_type : 'loose'; // loose, strict, none
    this._options.timeFormat = (config.timeFormat != null) ? config.timeFormat : 'native';
    this._options.validate = (config.validate != null) ? config.validate : 'onsave'; // 'onsave' or 'oncreate'

    this.r = rethinkdbdash(config);
    this.models = {};

    // Can we start using the database?
    this._dbReady = false;
    this._onDbReady = []; // functions to execute once the database is ready

    // Create the default database
    var r = this.r;
    r.dbList().contains(config.db).do(function(result) {
        return r.branch(
            result,
            {created: 1},
            r.dbCreate(config.db)
        )
    }).run().then(function(result) {
        self._dbReady = true;
    }).error(function(error) {
        throw error;
    });
}

Thinky.prototype.getOptions = function() {
    return this._options;
}

Thinky.prototype.createModel = function(name, schema, options) {
    var self = this;

    options = options || {};

    if (self.models[name] !== undefined) {
        throw new Error("Cannot redefine a Model");
    }

    model = Model.new(name, schema, options, self);
    self.models[name] = model;

    if (options.init !== false) {
        // Create the table, or push the table name in the queue.
        if (self._dbReady) {
            self.r.tableCreate(name).run().then(function(result) {
                model._setReady();
            }).error(function(error) {
                if (error.message.match(/^Database .* already exists.*/)) {
                    model._setReady();
                }
                else {
                    model._error = error;
                }
            })
        }
        else {
            self._onDbReady.push(function() {
                self.r.tableCreate(name).run().then(function(result) {
                    model._setReady();
                }).error(function(error) {
                    if (error.message.match(/^Database .* already exists.*/)) {
                        model._setReady();
                    }
                    else {
                        model._error = error;
                    }
                })
            });
        }
    }

    return model;
}

module.exports = function(config) {
    return new Thinky(config);
}
<commit_msg>Improve table creation<commit_after>var rethinkdbdash = require('rethinkdbdash');
var Model = require(__dirname+'/model.js');

/*
 * Main method
 * Create the default database
 *
 * Fields in `options` can be:
 *  - `min`: <number> - minimum number of connections in the pool, default 50
 *  - `max`: <number> -  maximum number of connections in the pool, default 1000
 *  - `bufferSize`: <number> - minimum number of connections available in the pool, default 50
 *  - `timeoutError`: <number> - wait time before reconnecting in case of an error (in ms), default 1000
 *  - `timeoutGb`: <number> - how long the pool keep a connection that hasn't been used (in ms), default 60*60*1000
 *  - `enforce`: {missing: <boolean>, extra: <boolean>, type: <boolean>}
 *  - `timeFormat`: "raw" or "native"
 *
 */
function Thinky(config) {
    var self = this;

    config = config || {};
    config.db = config.db || 'test'; // We need the default db to create it.

    this._options = {}
    this._options.enforce_missing = (config.enforce_missing != null) ? config.enforce_missing : false;
    this._options.enforce_extra = (config.enforce_extra != null) ? config.enforce_extra : false;
    this._options.enforce_type = (config.enforce_type != null) ? config.enforce_type : 'loose'; // loose, strict, none
    this._options.timeFormat = (config.timeFormat != null) ? config.timeFormat : 'native';
    this._options.validate = (config.validate != null) ? config.validate : 'onsave'; // 'onsave' or 'oncreate'

    this.r = rethinkdbdash(config);
    this.models = {};

    // Can we start using the database?
    this._dbReady = false;
    this._onDbReady = []; // functions to execute once the database is ready

    // Create the default database
    var r = this.r;
    r.dbList().contains(config.db).do(function(result) {
        return r.branch(
            result,
            {created: 1},
            r.dbCreate(config.db)
        )
    }).run().then(function(result) {
        self._dbReady = true;
    }).error(function(error) {
        throw error;
    });
}

Thinky.prototype.getOptions = function() {
    return this._options;
}

Thinky.prototype.createModel = function(name, schema, options) {
    var self = this;

    options = options || {};

    if (self.models[name] !== undefined) {
        throw new Error("Cannot redefine a Model");
    }

    model = Model.new(name, schema, options, self);
    self.models[name] = model;

    if (options.init !== false) {
        // Create the table, or push the table name in the queue.
        var r = self.r;
        if (self._dbReady) {
            r.tableList().contains(name).do(function(result) {
                return r.branch(
                    result,
                    {created: 1},
                    r.tableCreate(name)
                )
            }).run().then(function(result) {
                model._setReady();
            }).error(function(error) {
                model._error = error;
            })
        }
        else {
            self._onDbReady.push(function() {
                self.r.tableCreate(name).run().then(function(result) {
                    model._setReady();
                }).error(function(error) {
                    if (error.message.match(/^Database .* already exists.*/)) {
                        model._setReady();
                    }
                    else {
                        model._error = error;
                    }
                })
            });
        }
    }

    return model;
}

module.exports = function(config) {
    return new Thinky(config);
}
<|endoftext|>


<commit_before>var trelloHelper = (function() {

  var getBoardID = function() {
    var pathname = window.location.pathname;
    var boardID = /\/b\/(\w*)(\/*)/.exec(pathname);
    if(!boardID){
      alert('Not a Trello board!');
      throw 'Cannot detect Trello board';
    }
    return boardID[1];
  };

  var cardFieldArray = function(){
    fields = ['badges','closed','dateLastActivity','desc','descData','due',
    'idAttachmentCover','idList','idBoard','idMembers','idShort',
    'labels','idLabels','name','pos','shortUrl','shortLink',
    'subscribed','url'
    ];
    return fields;
  };

  var getCardFields = function(all=true){
    var fields = [];
    if(all == true)
      fields = cardFieldArray();
    else
      fields = ['idList','name','url'];
    var fieldText = 'card_fields=';
    for(var i=0; i < fields.length ; i++){
      fieldText += '%2C' + fields[i];
      if(i + 1 < fields.length)
        fieldText += '%2C';
    }
    return fieldText;
  };

  var getAPIEndpoint = function(boardID){
    return 'https://trello.com/1/boards/' +
           boardID +
           '?lists=open&cards=open&card_attachments=cover&card_stickers=true&'
           + 'card_pluginData=true&'
           + 'pluginData=true&'
           + getCardFields()
           + '&card_checklists=none&members=all&member_fields=fullName%2C'
           + 'initials%2CmemberType%2Cusername%2CavatarHash%2Cbio%2CbioData%2C'
           + 'confirmed%2Cproducts%2Curl%2Cstatus&membersInvited=all&'
           + 'membersInvited_fields=fullName%2Cinitials%2CmemberType%2C'
           + 'username%2CavatarHash%2Cbio%2CbioData%2Cconfirmed%2Cproducts%2'
           + 'Curl&checklists=none&organization=true&'
           + 'organization_fields=name%2CdisplayName%2Cdesc%2CdescData%2C'
           + 'url%2Cwebsite%2Cprefs%2Cmemberships%2ClogoHash%2Cproducts&'
           + 'myPrefs=true&fields=name%2Cclosed%2CdateLastActivity%2C'
           + 'dateLastView%2CidOrganization%2Cprefs%2CshortLink%2CshortUrl%2C'
           + 'url%2Cdesc%2CdescData%2Cinvitations%2Cinvited%2ClabelNames%2C'
           + 'memberships%2Cpinned%2CpowerUps%2Csubscribed'
  };

  var getBoardData = function(boardID) {
    var boardData = [];
    utils.loadJSON(
      getAPIEndpoint(boardID),
      function(data) {
        boardData = data;
      },
      function(xhr) {
        alert('Error in receiving list and card data.'+xhr);
        throw 'Failed to receive data from Trello';
      }
    );
    return boardData;
  };

  var boardID = getBoardID();
  var boardData = getBoardData(boardID);
  var listData = boardData.lists;
  var cardData = boardData.cards;
  var memberData = boardData.members;
  var pluginData = boardData.pluginData;

  return {

    data: function() {
      return {
        boardID: boardID,
        boardData: boardData,
        listData: listData,
        cardData: cardData,
        pluginData: pluginData
      }
    },

    cardToArray: function cardToArray(){
      var cards = cardData;
      var cardArray = [];
      cards.forEach(function(card){
        var singleCard = trelloHelper.cardToString(card);
        cardArray.push(singleCard);
      });
      return cardArray;
    },

    // Plugin ID of Custom Fields plugin is 56d5e249a98895a9797bebb9
    customFieldPluginData: function(){
      var customFieldsData = null;
      pluginData.forEach(function(plugin){
        if(plugin.idPlugin === "56d5e249a98895a9797bebb9")
          customFieldsData = JSON.parse(plugin.value);
      });
      return customFieldsData;
    },

    customFieldPluginFieldsArray: function(){
      var pluginData = trelloHelper.customFieldPluginData();
      if(pluginData === null)
        return [];

      var fields = [];
      var fieldsArray = pluginData.fields;
      fields = fieldsArray.map(function(field){
        return field.n;
      });
      return fields.sort();
    },

    customFieldPluginLookUp: function(){
      var pluginData = trelloHelper.customFieldPluginData();
      if(pluginData === null)
        return {};

      var lookUp = {};
      var fieldsArray = pluginData.fields;
      fieldsArray.forEach(function(field){
        lookUp[field.n] = field.id;
      });
      return lookUp;
    },

    customFieldPluginColNames: function(){
      var fieldsArray = trelloHelper.customFieldPluginFieldsArray();
      fields = fieldsArray.map(function(field){
        return 'Custom Field: ' + field;
      });
      return fields;
    },

    getCustomFieldDataValue: function(card, customFieldName){
      var pluginData = card.pluginData;
      var customFieldId = trelloHelper.customFieldPluginLookUp()[customFieldName];
      var customFieldDataValue = ""

      pluginData.forEach(function(plugin){
        if(plugin.idPlugin !== "56d5e249a98895a9797bebb9")
          return;
        var fields = JSON.parse(plugin.value).fields;
        if(customFieldId in fields)
          customFieldDataValue = fields[customFieldId];
      });

      return customFieldDataValue;
    },

    columnFullOrder: function(){
      return [
        'id',
        'createdAt',
        'closed',
        'dateLastActivity',
        'idList',
        'name',
        'points',
        'desc',
        'idMembers',
        'labels',
        'due',
        'shortUrl',
        'url',
        'pos',
        'idBoard',
        'idShort',
        'shortLink',
        'subscribed',
        'badges',
        'idLabels',
        'stickers',
        'descData',
        'idAttachementCover',
        'attachments'
      ].concat(trelloHelper.customFieldPluginColNames())
    },

    cardToString: function processCard(card){
      var singleCard = '';
      var headers = trelloHelper.columnFullOrder();
      for(var i=0; i < headers.length; i++){
        var header = headers[i];
        var content = card[header];
        var cleanStr =  (''+[content]).replace(/"/g,"'");
        if(header.match(/^Custom\sField:/)){
          var customFieldDataName = header.match(/^Custom\sField:\s*(.*)\s*$/)[1];
          var customFieldDataValue = trelloHelper.getCustomFieldDataValue(card, customFieldDataName);
          singleCard += '"'+customFieldDataValue+'"';
        } else {
          switch (header) {
            case 'id':
              var idCard = cleanStr;
              var createdAt = new Date(1000*parseInt(idCard.substring(0,8),16));
              singleCard += '"'+cleanStr+'"'+',"'+createdAt+'"';
              break;
            case 'createdAt':
              break;
            case 'idList':
              var listLookUp = trelloHelper.getListLookUp(listData);
              singleCard += '"'+listLookUp[content]+'"';
              break;
            case 'idMembers':
              singleCard += '"'+trelloHelper.processMemberIds(content)+'"';
              break;
            case 'name':
              var name = cleanStr;
              var pointReg = /[\(](\x3f|\d*\.?\d+)([\)])\s?/m;
              var parsed = name.match(pointReg);
              var points = parsed ? parsed[1] : '';
              var cleanName = name.replace(pointReg, '');
              singleCard += '"'+cleanName+'"'+',"'+points+'"';
              break;
            case 'labels':
              var labels = [];
              content.forEach( function (label){
                if (label.name)
                  labels.push(label.name);
                else
                  labels.push(label.color);
              });
              singleCard += '"'+labels.join(',')+'"';
              break;
            case 'points':
              break;
            default:
              singleCard += '"'+cleanStr+'"';
          }
        }

        if(i + 1 < headers.length && header != 'points' && header != 'createdAt')
          singleCard += ',';
      }
      return singleCard;
    },

    getCSVHeaders: function() {
      return trelloHelper.columnFullOrder().join(',');
    },

    getListLookUp: function getListLookUp(lists){
      var listLookUp = {};
      for(var key in lists){
        listLookUp[lists[key].id] = lists[key].name;
      }
      return listLookUp;
    },

    getMemberLookUp: function getMemberLookUp(members){
      var lookUp = {};
      for(var index in members){
        lookUp[members[index].id] = members[index].initials;
      }
      return lookUp;
    },

    processMemberIds: function(membersInCard){
      var memberLookUp = trelloHelper.getMemberLookUp(memberData);
      return membersInCard.map(memberId => memberLookUp[memberId]).join(', ');
    }

  };

})();
<commit_msg>Add in custom field dropdown list handling<commit_after>var trelloHelper = (function() {

  var getBoardID = function() {
    var pathname = window.location.pathname;
    var boardID = /\/b\/(\w*)(\/*)/.exec(pathname);
    if(!boardID){
      alert('Not a Trello board!');
      throw 'Cannot detect Trello board';
    }
    return boardID[1];
  };

  var cardFieldArray = function(){
    fields = ['badges','closed','dateLastActivity','desc','descData','due',
    'idAttachmentCover','idList','idBoard','idMembers','idShort',
    'labels','idLabels','name','pos','shortUrl','shortLink',
    'subscribed','url'
    ];
    return fields;
  };

  var getCardFields = function(all=true){
    var fields = [];
    if(all == true)
      fields = cardFieldArray();
    else
      fields = ['idList','name','url'];
    var fieldText = 'card_fields=';
    for(var i=0; i < fields.length ; i++){
      fieldText += '%2C' + fields[i];
      if(i + 1 < fields.length)
        fieldText += '%2C';
    }
    return fieldText;
  };

  var getAPIEndpoint = function(boardID){
    return 'https://trello.com/1/boards/' +
           boardID +
           '?lists=open&cards=open&card_attachments=cover&card_stickers=true&'
           + 'card_pluginData=true&'
           + 'pluginData=true&'
           + getCardFields()
           + '&card_checklists=none&members=all&member_fields=fullName%2C'
           + 'initials%2CmemberType%2Cusername%2CavatarHash%2Cbio%2CbioData%2C'
           + 'confirmed%2Cproducts%2Curl%2Cstatus&membersInvited=all&'
           + 'membersInvited_fields=fullName%2Cinitials%2CmemberType%2C'
           + 'username%2CavatarHash%2Cbio%2CbioData%2Cconfirmed%2Cproducts%2'
           + 'Curl&checklists=none&organization=true&'
           + 'organization_fields=name%2CdisplayName%2Cdesc%2CdescData%2C'
           + 'url%2Cwebsite%2Cprefs%2Cmemberships%2ClogoHash%2Cproducts&'
           + 'myPrefs=true&fields=name%2Cclosed%2CdateLastActivity%2C'
           + 'dateLastView%2CidOrganization%2Cprefs%2CshortLink%2CshortUrl%2C'
           + 'url%2Cdesc%2CdescData%2Cinvitations%2Cinvited%2ClabelNames%2C'
           + 'memberships%2Cpinned%2CpowerUps%2Csubscribed'
  };

  var getBoardData = function(boardID) {
    var boardData = [];
    utils.loadJSON(
      getAPIEndpoint(boardID),
      function(data) {
        boardData = data;
      },
      function(xhr) {
        alert('Error in receiving list and card data.'+xhr);
        throw 'Failed to receive data from Trello';
      }
    );
    return boardData;
  };

  var boardID = getBoardID();
  var boardData = getBoardData(boardID);
  var listData = boardData.lists;
  var cardData = boardData.cards;
  var memberData = boardData.members;
  var pluginData = boardData.pluginData;

  return {

    data: function() {
      return {
        boardID: boardID,
        boardData: boardData,
        listData: listData,
        cardData: cardData,
        pluginData: pluginData
      }
    },

    cardToArray: function cardToArray(){
      var cards = cardData;
      var cardArray = [];
      cards.forEach(function(card){
        var singleCard = trelloHelper.cardToString(card);
        cardArray.push(singleCard);
      });
      return cardArray;
    },

    // Plugin ID of Custom Fields plugin is 56d5e249a98895a9797bebb9
    customFieldPluginData: function(){
      var customFieldsData = null;
      pluginData.forEach(function(plugin){
        if(plugin.idPlugin === "56d5e249a98895a9797bebb9")
          customFieldsData = JSON.parse(plugin.value);
      });
      return customFieldsData;
    },

    customFieldPluginFieldsArray: function(){
      var pluginData = trelloHelper.customFieldPluginData();
      if(pluginData === null)
        return [];

      var fields = [];
      var fieldsArray = pluginData.fields;
      fields = fieldsArray.map(function(field){
        return field.n;
      });
      return fields.sort();
    },

    customFieldPluginLookUpByName: function(){
      var pluginData = trelloHelper.customFieldPluginData();
      if(pluginData === null)
        return {};

      var lookUp = {};
      var fieldsArray = pluginData.fields;
      fieldsArray.forEach(function(field){
        lookUp[field.n] = field;
      });
      return lookUp;
    },

    customFieldPluginLookUpById: function(){
      var pluginData = trelloHelper.customFieldPluginData();
      if(pluginData === null)
        return {};

      var lookUp = {};
      var fieldsArray = pluginData.fields;
      fieldsArray.forEach(function(field){
        lookUp[field.id] = field;
      });
      return lookUp;
    },

    customFieldPluginColNames: function(){
      var fieldsArray = trelloHelper.customFieldPluginFieldsArray();
      fields = fieldsArray.map(function(field){
        return 'Custom Field: ' + field;
      });
      return fields;
    },

    getCustomFieldDropdownOptionValue: function(customFieldId, optionId){
      var optionValue = "";
      var dropdownOptionArray = trelloHelper.customFieldPluginLookUpById()[customFieldId]["o"];
      for(let i=0; i < dropdownOptionArray.length; i++){
        if(dropdownOptionArray[i]["id"] === optionId){
          optionValue = dropdownOptionArray[i]["value"];
          break;
        }
      }
      return optionValue;
    },

    getCustomFieldDataValue: function(card, customFieldName){
      var pluginData = card.pluginData;
      var customFieldId = trelloHelper.customFieldPluginLookUpByName()[customFieldName]["id"];
      var customFieldDataValue = ""

      pluginData.forEach(function(plugin){
        if(plugin.idPlugin !== "56d5e249a98895a9797bebb9")
          return;
        var fields = JSON.parse(plugin.value).fields;
        if(customFieldId in fields){
          // Type = 4 is a dropdown list, which data is in an array
          if(trelloHelper.customFieldPluginLookUpByName()[customFieldName]["t"] === 4){
            customFieldDataValue = 'list';
            customFieldDataValue = trelloHelper.getCustomFieldDropdownOptionValue(customFieldId, fields[customFieldId]);
          } else {
            customFieldDataValue = fields[customFieldId];
          }
        }
      });

      return customFieldDataValue;
    },

    columnFullOrder: function(){
      return [
        'id',
        'createdAt',
        'closed',
        'dateLastActivity',
        'idList',
        'name',
        'points',
        'desc',
        'idMembers',
        'labels',
        'due',
        'shortUrl',
        'url',
        'pos',
        'idBoard',
        'idShort',
        'shortLink',
        'subscribed',
        'badges',
        'idLabels',
        'stickers',
        'descData',
        'idAttachementCover',
        'attachments'
      ].concat(trelloHelper.customFieldPluginColNames())
    },

    cardToString: function processCard(card){
      var singleCard = '';
      var headers = trelloHelper.columnFullOrder();
      for(var i=0; i < headers.length; i++){
        var header = headers[i];
        var content = card[header];
        var cleanStr =  (''+[content]).replace(/"/g,"'");
        if(header.match(/^Custom\sField:/)){
          var customFieldDataName = header.match(/^Custom\sField:\s*(.*)\s*$/)[1];
          var customFieldDataValue = trelloHelper.getCustomFieldDataValue(card, customFieldDataName);
          singleCard += '"'+customFieldDataValue+'"';
        } else {
          switch (header) {
            case 'id':
              var idCard = cleanStr;
              var createdAt = new Date(1000*parseInt(idCard.substring(0,8),16));
              singleCard += '"'+cleanStr+'"'+',"'+createdAt+'"';
              break;
            case 'createdAt':
              break;
            case 'idList':
              var listLookUp = trelloHelper.getListLookUp(listData);
              singleCard += '"'+listLookUp[content]+'"';
              break;
            case 'idMembers':
              singleCard += '"'+trelloHelper.processMemberIds(content)+'"';
              break;
            case 'name':
              var name = cleanStr;
              var pointReg = /[\(](\x3f|\d*\.?\d+)([\)])\s?/m;
              var parsed = name.match(pointReg);
              var points = parsed ? parsed[1] : '';
              var cleanName = name.replace(pointReg, '');
              singleCard += '"'+cleanName+'"'+',"'+points+'"';
              break;
            case 'labels':
              var labels = [];
              content.forEach( function (label){
                if (label.name)
                  labels.push(label.name);
                else
                  labels.push(label.color);
              });
              singleCard += '"'+labels.join(',')+'"';
              break;
            case 'points':
              break;
            default:
              singleCard += '"'+cleanStr+'"';
          }
        }

        if(i + 1 < headers.length && header != 'points' && header != 'createdAt')
          singleCard += ',';
      }
      return singleCard;
    },

    getCSVHeaders: function() {
      return trelloHelper.columnFullOrder().join(',');
    },

    getListLookUp: function getListLookUp(lists){
      var listLookUp = {};
      for(var key in lists){
        listLookUp[lists[key].id] = lists[key].name;
      }
      return listLookUp;
    },

    getMemberLookUp: function getMemberLookUp(members){
      var lookUp = {};
      for(var index in members){
        lookUp[members[index].id] = members[index].initials;
      }
      return lookUp;
    },

    processMemberIds: function(membersInCard){
      var memberLookUp = trelloHelper.getMemberLookUp(memberData);
      return membersInCard.map(memberId => memberLookUp[memberId]).join(', ');
    }

  };

})();
<|endoftext|>


<commit_before>
console.log("your mom")

// global variables galore
var current_tab = null;
var interestContext = null;
var ChartObject = null;

// week select function
function week_select (e) {
    e.preventDefault()
    // generate graph data here
    var computed_data = []
    // need to calculate how far back to look
    // get current date and subtract 7 days (604800000 ms)
    var today = new Date();
    var today_ms = Date.parse(today); // get today in ms since sometime in 1970
    var past_ms = today_ms - 604800000;
    var past = new Date(past_ms);
    // based on currently selected tab, selector is going to vary
    var data_pointer = null;
    if(current_tab == "spending") {
        items.find({date_acquired: {$gte: past}})
    } else if (current_tab == "stock") {

    } else if (current_tab == "waste") {

    } else {
        alert("error! error! D: wahhh")
    }

    var data = {
        labels : ["6 days","5 days","4 days","3 days","2 days","1 day","today"],
        datasets : [
        {
            fillColor : "rgba(151,187,205,0.5)",
            strokeColor : "rgba(151,187,205,1)",
            pointColor : "rgba(151,187,205,1)",
            pointStrokeColor : "#fff",
            data : computed_data
        }
        ]
    }

    $("#chart").attr('width', '300px');
    $("#chart").attr('height', '300px');

    ChartObject.Line(data);

    $("#spendingLabel").html("Spent $43");
    $("#stockLabel").html("Had 17 items");
    $("#wasteLabel").html("Lost $43");
    $("#dropdown_title").html("Past week");

}

$(window).load(function () {
    console.log("got here")

    interestContext = $("#chart").get(0).getContext("2d");
    ChartObject = new Chart(interestContext);

    // all the odds and ends
	var dropdown = $("#timeSelectDropdown");
	$("#timeSelectDropdownMenu").offset({left: dropdown.offset().left});
    $('#timeOptions li').on('click', function() {
        $('#dropdown_title').html("Last ".concat($(this).find('a').html()));
    });
    $("#chart").attr('width', '300px');
    $("#chart").attr('height', '300px');

    // code for handling tab switches
    // needs to call timeframe select method upon switch
    $('#spending').click(function (e) {
    	e.preventDefault()
        current_tab = "spending"
    	$(this).tab('show')
    })
    $('#stock').click(function (e) {
    	e.preventDefault()
        current_tab = "stock"
    	$(this).tab('show')
    })
    $('#waste').click(function (e) {
    	e.preventDefault()
        current_tab = "waste"
    	$(this).tab('show')
    })
    $('#weekSelect').click()
    $('#monthSelect').click(function (e) {
    	e.preventDefault()
        // generate graph data here
        var data = {
        	labels : ["1 month","3 weeks","2 weeks","1 week","today"],
        	datasets : [
        	{
        		fillColor : "rgba(151,187,205,0.5)",
        		strokeColor : "rgba(151,187,205,1)",
        		pointColor : "rgba(151,187,205,1)",
        		pointStrokeColor : "#fff",
        		data : [80,50,23,11,43]
        	}
        	]
        }
        ChartObject.Line(data);

        $("#spendingLabel").html("Spent $163");
        $("#stockLabel").html("Had 33 items");
        $("#wasteLabel").html("Lost $45");
        $("#dropdown_title").html("Past month");
    })
    $('#threeMonthSelect').click(function (e) {
    	e.preventDefault()
        // generate graph data here
        var data = {
        	labels : ["3 months","2 months","1 month","today"],
        	datasets : [
        	{
        		fillColor : "rgba(151,187,205,0.5)",
        		strokeColor : "rgba(151,187,205,1)",
        		pointColor : "rgba(151,187,205,1)",
        		pointStrokeColor : "#fff",
        		data : [80,70,95,44,12]
        	}
        	]
        }
        ChartObject.Line(data);

        $("#spendingLabel").html("Spent $344");
        $("#stockLabel").html("Had 65 items");
        $("#wasteLabel").html("Lost $99");
        $("#dropdown_title").html("Past 3 months");
    })

    ChartObject.Line(data);

    $("#spendingLabel").html("Spent $43")
    $("#stockLabel").html("Had 17 items")
    $("#wasteLabel").html("Lost $43")
    $("#dropdown_title").html("Past week");
});<commit_msg>Changed this back<commit_after>
console.log("your mom")

// global variables galore
var current_tab = null;
var interestContext = null;
var ChartObject = null;

// week select function
function week_select (e) {
    e.preventDefault()
    // generate graph data here
    var computed_data = []
    // need to calculate how far back to look
    // get current date and subtract 7 days (604800000 ms)
    var today = new Date();
    var today_ms = Date.parse(today); // get today in ms since sometime in 1970
    var past_ms = today_ms - 604800000;
    var past = new Date(past_ms);
    // based on currently selected tab, selector is going to vary
    var data_pointer = null;
    if(current_tab == "spending") {
        items.find({date_acquired: {$gte: past}})
    } else if (current_tab == "stock") {

    } else if (current_tab == "waste") {

    } else {
        alert("error! error! D: wahhh")
    }

    var data = {
        labels : ["6 days","5 days","4 days","3 days","2 days","1 day","today"],
        datasets : [
        {
            fillColor : "rgba(151,187,205,0.5)",
            strokeColor : "rgba(151,187,205,1)",
            pointColor : "rgba(151,187,205,1)",
            pointStrokeColor : "#fff",
            data : computed_data
        }
        ]
    }

    $("#chart").attr('width', '300px');
    $("#chart").attr('height', '300px');

    ChartObject.Line(data);

    $("#spendingLabel").html("Spent $43");
    $("#stockLabel").html("Had 17 items");
    $("#wasteLabel").html("Lost $43");
    $("#dropdown_title").html("Past week");

}

$(document).ready(function () {
    console.log("got here")

    interestContext = $("#chart").get(0).getContext("2d");
    ChartObject = new Chart(interestContext);

    // all the odds and ends
	var dropdown = $("#timeSelectDropdown");
	$("#timeSelectDropdownMenu").offset({left: dropdown.offset().left});
    $('#timeOptions li').on('click', function() {
        $('#dropdown_title').html("Last ".concat($(this).find('a').html()));
    });
    $("#chart").attr('width', '300px');
    $("#chart").attr('height', '300px');

    // code for handling tab switches
    // needs to call timeframe select method upon switch
    $('#spending').click(function (e) {
    	e.preventDefault()
        current_tab = "spending"
    	$(this).tab('show')
    })
    $('#stock').click(function (e) {
    	e.preventDefault()
        current_tab = "stock"
    	$(this).tab('show')
    })
    $('#waste').click(function (e) {
    	e.preventDefault()
        current_tab = "waste"
    	$(this).tab('show')
    })
    $('#weekSelect').click()
    $('#monthSelect').click(function (e) {
    	e.preventDefault()
        // generate graph data here
        var data = {
        	labels : ["1 month","3 weeks","2 weeks","1 week","today"],
        	datasets : [
        	{
        		fillColor : "rgba(151,187,205,0.5)",
        		strokeColor : "rgba(151,187,205,1)",
        		pointColor : "rgba(151,187,205,1)",
        		pointStrokeColor : "#fff",
        		data : [80,50,23,11,43]
        	}
        	]
        }
        ChartObject.Line(data);

        $("#spendingLabel").html("Spent $163");
        $("#stockLabel").html("Had 33 items");
        $("#wasteLabel").html("Lost $45");
        $("#dropdown_title").html("Past month");
    })
    $('#threeMonthSelect').click(function (e) {
    	e.preventDefault()
        // generate graph data here
        var data = {
        	labels : ["3 months","2 months","1 month","today"],
        	datasets : [
        	{
        		fillColor : "rgba(151,187,205,0.5)",
        		strokeColor : "rgba(151,187,205,1)",
        		pointColor : "rgba(151,187,205,1)",
        		pointStrokeColor : "#fff",
        		data : [80,70,95,44,12]
        	}
        	]
        }
        ChartObject.Line(data);

        $("#spendingLabel").html("Spent $344");
        $("#stockLabel").html("Had 65 items");
        $("#wasteLabel").html("Lost $99");
        $("#dropdown_title").html("Past 3 months");
    })

    ChartObject.Line(data);

    $("#spendingLabel").html("Spent $43")
    $("#stockLabel").html("Had 17 items")
    $("#wasteLabel").html("Lost $43")
    $("#dropdown_title").html("Past week");
});<|endoftext|>


<commit_before>var _ = require('lodash'),
	acorn = require('acorn'),
	async = require('async'),
	constants = require('./constants'),
	fileServer = require('./fileServer'),
	fs = require('fs-extra'),
	logger = require('./logger'),
	os = require('os'),
	path = require('path'),
	readline = require('readline'),
	request = require('request'),
	server = require('./server'),
	spinner = require('char-spinner'),
	tiappXml = require('tiapp.xml'),
	titanium = require('./titanium'),
	util = require('./util');

var TRIPLE_DIR = path.resolve(process.env.HOME || process.env.USERPROFILE || '.', '.triple');
var TRIPLE_APP = path.join(TRIPLE_DIR, 'app');
var PLATFORMS = ['android', 'ios'];

var requireRE = /(\brequire\s*\(\s*['"])([^'"]+)(['"]\s*\))/,
	simpleExpressionRE = /(([a-zA-Z_$](?:\w|\$)*)\.)*([a-zA-Z_$](?:\w|\$)*)\.?$/,
	tabCompletionNS = {};

module.exports = function(opts, callback) {
	var buffer = [];

	callback = arguments[arguments.length-1];
	if (!opts || _.isFunction(opts)) {
		opts = {};
	}

	// setup logging
	if (opts.verbose) {
		titanium.verbose = true;
		logger.verbose = true;
	}

	// determine which platform to build
	opts.platform = opts.platform || (os.platform() === 'darwin' ? 'ios' : 'android');
	if (PLATFORMS.indexOf(opts.platform) === -1) {
		return callback(new Error('invalid platform "' + opts.platform +
			'". Must be [' + PLATFORMS.join(',') + '].'));
	}
	util.platform = opts.platform;
	constants.DIR_PROP = opts.platform === 'ios' ? 'resourcesDirectory' :
		'applicationDataDirectory';

	// create .triple folder
	fs.ensureDirSync(TRIPLE_DIR);

	// create readline interface
	var rl = readline.createInterface({
		input: process.stdin,
		output: process.stdout,
		completer: tabCompletion(rl)
	});

	async.series([

		// make sure we're logged in
		function(cb) {
			titanium.loggedIn(function(err, loggedIn) {
				if (err) {
					err.message = 'error checking login status: ' + (err.message || '')
					return cb(err);
				} else if (!loggedIn) {
					return cb(new Error('You must be logged in to use triple. Use `titanium login`.'));
				}
				return cb();
			});
		},

		// setup titanium completion database
		function (cb) {
			titanium.sdk(function(err,sdk, version, info){
				var cacheFile = path.join(sdk.path,'triple.cache');
				if (fs.existsSync(cacheFile)) {
					return fs.readFile(cacheFile, function(err,contents){
						contents && (tabCompletionSetup(JSON.parse(contents.toString())));
						cb(err);
					});
				}
				var interval = spinner();
				fs.readFile(path.join(sdk.path,'api.jsca'), function(err,buf){
					if (err) {
						clearInterval(interval);
						logger.warn('warn: could not find api.jsca file, auto-complete disabled');
						return cb();
					}
					var tabCompletionDB = [];
					JSON.parse(buf.toString()).types.forEach(function(entry){
						tabCompletionDB.push(entry.name);
						entry.properties && entry.properties.forEach(function(prop){
							tabCompletionDB.push(entry.name+'.'+prop.name);
						});
						entry.functions && entry.functions.forEach(function(fn){
							tabCompletionDB.push(entry.name+'.'+fn.name);
						});
					});
					fs.writeFile(cacheFile, JSON.stringify(tabCompletionDB), function(err){
						clearInterval(interval);
						tabCompletionSetup(tabCompletionDB);
						cb(err);
					});
				});
			});
		},

		// create the repl project
		function(cb) {
			if (opts.clean) {
				fs.removeSync(TRIPLE_APP);
			}

			if (!fs.existsSync(TRIPLE_APP)) {
				logger.system('creating app');
				var interval = spinner();
				var createOpts = _.defaults(opts.create || {}, {
					name: 'app',
					id: 'triple.tmpapp',
					workspaceDir: TRIPLE_DIR,
					platform: ['android','ios'],
					logLevel: 'error'
				});
				titanium.create(createOpts, function(err, results) {
					clearInterval(interval);
					if (err && !opts.verbose) {
						return cb(new Error('create failed: try with --verbose for more information.'));
					}
					cb(err, results);
				});
			} else {
				cb();
			}
		},

		// prep app
		function(cb) {
			var resources = path.join(TRIPLE_APP, 'Resources'),
				app = path.join(__dirname, '..', 'app');

			// copy in all app files
			fs.readdirSync(app).forEach(function(file) {
				copy(path.join(app, file), path.join(resources, file));
			});

			// copy in shared constants file
			copy(path.join(__dirname, 'constants.js'), path.join(resources, 'constants.js'));

			// load tiapp.xml
			var tiapp = tiappXml.load(path.resolve(TRIPLE_APP, 'tiapp.xml'));

			// disable analytics
			tiapp.analytics = false;

			// load native modules into tiapp.xml
			if(opts.module) {
				opts.module.forEach(function(id) {
					logger.system('Injecting module: %s', id);
					tiapp.setModule(id);
				});
			}

			tiapp.write();

			cb();
		},

		// // start up repl server
		function(cb) {
			server.on('end', function(data) {
				return cb(new Error(data));
			});
			server.listen(constants.PORT, cb);
		},

		// start up file server
		function(cb) {
			//if (opts.platform === 'ios') { return cb(); }
			fileServer.on('end', function(data) {
				return cb(new Error(data));
			});
			fileServer.listen(constants.PORT_FILE, cb);
		},

		// build the repl project
		function(cb) {
			logger.system('launching app');
			var interval = spinner();
			var buildOpts = _.defaults(opts.build || {}, {
				projectDir: TRIPLE_APP,
				platform: opts.platform,
				server: server
			});
			if (opts.platform === 'ios') { buildOpts.noSimFocus = true; }

			titanium.build(buildOpts, function(err) {
				if (err) {
					if (!opts.verbose) {
						return cb(new Error('build failed: try with --verbose for more information.'));
					} else {
						return cb(err);
					}
				}
			});
			server.on('ready', function() {
				clearInterval(interval);
				cb();
			});
		},

		// prompt
		function(cb) {

			// clear spinner and prompt
			process.stdout.write('\r \r');
			rl.setPrompt(constants.PROMPT);
			rl.prompt();

			// throttle incoming code
			var queue = [],
				interval;
			rl.on('line', function(line) {
				interval && clearTimeout(interval);
				queue.push(line);
				interval = setTimeout(processQueue, 100);
			});

			// handle each line of input
			function processQueue() {

				// turn queued lines into a single message
				var match;
				var line = queue.join('\n');
				queue = [];

				// move cursor to next line if necessary
				if (rl.cursor) {
					process.stdout.write('\n');
				}

				// skip empty lines
				if (!line) {
					rl.prompt();
					return;

				// handle triple commands
				} else if (match = line.match(/^\s*(\.\w*)(?:\s|$)/)) {
					var args = match.input.trim().split(/\s+/).slice(1);

					// don't put triple commands on history
					rl.history.shift();

					// execute the command
					switch(match[1].substring(1)) {
						case 'add':
							for (var i = 0; i < args.length; i++) {
								util.addToApp(args[i]);
							}
							break;
						case 'break':
							buffer = [];
							rl.setPrompt(constants.PROMPT);
							break;
						case 'clear':
							server.emit('message', constants.CLEAR_MESSAGE);
							server.resetInterval = spinner();
							return; // wait for context reset message from app before prompting again
						case 'help':
							util.printCommands(0);
							break;
						case 'load':
							load(args, rl);
							break;
						case 'exit':
							return cb();
						case 'save':
							var file = args[0];
							if (!file) {
								logger.error('must specify path for save');
							} else {
								fs.ensureDir(path.dirname(file));
								fs.writeFileSync(file, rl.history.slice(0).reverse().join('\n'));
							}
							break;
						default:
							logger.error('invalid command "' + match[1] + '"');
							break;
					}
					rl.prompt();

				// process code
				} else {
					try {

						var origLine = line;

						// update paths to file names
						if (opts.platform !== 'ios' && util.fileRx) {
							line = line.replace(util.fileRx, 'Ti.Filesystem.' + constants.DIR_PROP + '+$1');
						}

						// if it contains a require(), but isn't a native require
						if (opts.platform === 'ios' && (match = line.match(requireRE))
							&& !_.contains(opts.module, match[2]))
						{
							var moduleId = match[2],
								modulePath;

							// get absolute path
							if (moduleId.indexOf('/') === 0) {
								modulePath = moduleId;
							} else {
								modulePath = path.join(process.cwd(), moduleId);
							}

							// .add it
							var addPath = modulePath + (!/\.js$/.test(modulePath) ? '.js' : '');
							util.addToApp(addPath);

							// modify the line before sending it to the app
							line = line.substring(0, match.index) + match[1] + '__modules' + modulePath +
								match[3] + line.substring(match.index + match[0].length);

						}

						// did the line change?
						if (origLine !== line) {
							logger.debug(constants.PROMPT + line);
						}

						// assemble code from buffer
						buffer.push(line);
						var code = buffer.join('\n');

						// validate the buffered code
						acorn.parse(code);

						// send code to server
						server.emit('message', code);

						// reset buffer and prompt
						buffer = [];
						rl.setPrompt(constants.PROMPT);
					} catch (e) {

						// prompt for multi-line statement
						rl.setPrompt(constants.CONTINUE_PROMPT);
						rl.prompt();
					}
				}
			}

			// handle CTRL+C
			rl.on('SIGINT', function() {

				// If we're in a multi-line statement, break out of it
				if (buffer.length) {
					buffer = [];
					logger.log('\n(^C again to quit)');
					rl.setPrompt(constants.PROMPT);
					rl.prompt();

				// quit
				} else {
					cb();
				}
			});

			// do we have a file to load?
			if (opts.args && opts.args.length) {
				load(opts.args, rl);
			}

		}

	], callback);
};

function copy(src, dst) {
	fs.writeFileSync(dst, fs.readFileSync(src));
}

function sendLoadCommand(rl, location, content, delay) {
	try {
		acorn.parse(content);
	} catch (err) {
		logger.error('error parsing "' + location + '"');
		logger.error(err);
		return rl.prompt();
	}

	var tasks = [];
	content.split('\n').forEach(function(line){
		line = line.trim();
		if (line) {
			tasks.push(function(next){
				setTimeout(function(){
					rl.write(line + '\n');
					next();
				}, delay || 25);
			});
		}
	});
	return async.series(tasks);
}

function load(tokens, rl) {
	var location = tokens[0],
		delay = tokens[1] || 0;

	// looks like a url, fetch it
	if (/^https?:\/\//.test(location)) {
		var interval = spinner();
		return request(location, function(err,content) {
			clearInterval(interval);
			if (err) {
				logger.error('error loading "' + location + '"');
				logger.error(err);
				rl.prompt();
				return;
			}
			sendLoadCommand(rl, location, content.body, delay);
		});

	// probably a file
	} else {
		var fullpath = path.resolve(process.cwd(), location);
		if (fs.existsSync(fullpath)) {

			// if it's a folder
			if (fs.lstatSync(fullpath).isDirectory()) {

				// try app.js and/or index.js
				var tries = ['app.js', 'index.js'];
				for (var i = 0; i < tries.length; i++) {
					var file = path.join(fullpath, tries[i]);
					if (fs.existsSync(file)) {
						return sendLoadCommand(rl, file, fs.readFileSync(file, 'utf8'), delay);
					}
				}
			}

			// if it's a file
			else {
				return sendLoadCommand(rl, fullpath, fs.readFileSync(fullpath, 'utf8'), delay);
			}
		}
	}

	logger.error('not sure how to load "' + location + '". does it exist?');
	rl.prompt();
}

function tabCompletionSetup(tabCompletionDB) {
	if (tabCompletionDB && tabCompletionDB.length) {
		tabCompletionDB.forEach(function(name){
			var tokens = name.split('.'),
				obj = tabCompletionNS;
			for (var c=0;c<tokens.length;c++){
				var cname = tokens[c];
				var entry = obj[cname];
				if (!entry) {
					obj[cname] = entry = {};
				}
				obj = entry;
			}
		});
		tabCompletionDB = null;
		tabCompletionNS.Ti = tabCompletionNS.Titanium;
	}
}

function tabCompletion(rl) {
	return function(line) {

		var match = line.match(simpleExpressionRE);
		if (!match) { return [[],line]; }
		line = match[0];

		var tokens = line.split('.'),
			obj = tabCompletionNS,
			hits = [];

		for (var c=0;c<tokens.length;c++) {
			var token = tokens[c];
			if (token) {
				var entry = obj[token];
				if (!entry) {
					hits = Object.keys(obj).filter(function(e){
						return e.indexOf(token)===0;
					});
					obj = null;
					line = token;
					break;
				}
				obj = entry;
			}
		}

		if (obj) {
			hits = Object.keys(obj);
		}

		return [!hits.length && !line ? Object.keys(tabCompletionNS) : hits, line];
	};
}
<commit_msg>better error checking on sdk completion database<commit_after>var _ = require('lodash'),
	acorn = require('acorn'),
	async = require('async'),
	constants = require('./constants'),
	fileServer = require('./fileServer'),
	fs = require('fs-extra'),
	logger = require('./logger'),
	os = require('os'),
	path = require('path'),
	readline = require('readline'),
	request = require('request'),
	server = require('./server'),
	spinner = require('char-spinner'),
	tiappXml = require('tiapp.xml'),
	titanium = require('./titanium'),
	util = require('./util');

var TRIPLE_DIR = path.resolve(process.env.HOME || process.env.USERPROFILE || '.', '.triple');
var TRIPLE_APP = path.join(TRIPLE_DIR, 'app');
var PLATFORMS = ['android', 'ios'];

var requireRE = /(\brequire\s*\(\s*['"])([^'"]+)(['"]\s*\))/,
	simpleExpressionRE = /(([a-zA-Z_$](?:\w|\$)*)\.)*([a-zA-Z_$](?:\w|\$)*)\.?$/,
	tabCompletionNS = {};

module.exports = function(opts, callback) {
	var buffer = [];

	callback = arguments[arguments.length-1];
	if (!opts || _.isFunction(opts)) {
		opts = {};
	}

	// setup logging
	if (opts.verbose) {
		titanium.verbose = true;
		logger.verbose = true;
	}

	// determine which platform to build
	opts.platform = opts.platform || (os.platform() === 'darwin' ? 'ios' : 'android');
	if (PLATFORMS.indexOf(opts.platform) === -1) {
		return callback(new Error('invalid platform "' + opts.platform +
			'". Must be [' + PLATFORMS.join(',') + '].'));
	}
	util.platform = opts.platform;
	constants.DIR_PROP = opts.platform === 'ios' ? 'resourcesDirectory' :
		'applicationDataDirectory';

	// create .triple folder
	fs.ensureDirSync(TRIPLE_DIR);

	// create readline interface
	var rl = readline.createInterface({
		input: process.stdin,
		output: process.stdout,
		completer: tabCompletion(rl)
	});

	async.series([

		// make sure we're logged in
		function(cb) {
			titanium.loggedIn(function(err, loggedIn) {
				if (err) {
					err.message = 'error checking login status: ' + (err.message || '')
					return cb(err);
				} else if (!loggedIn) {
					return cb(new Error('You must be logged in to use triple. Use `titanium login`.'));
				}
				return cb();
			});
		},

		// setup titanium completion database
		function (cb) {
			titanium.sdk(function(err, sdk, version, info){
				if (err || !sdk) {
					logger.warn('warn: could not find api.jsca file, auto-complete disabled');
					return cb();
				}

				var cacheFile = path.join(sdk.path,'triple.cache');
				if (fs.existsSync(cacheFile)) {
					return fs.readFile(cacheFile, function(err,contents){
						contents && (tabCompletionSetup(JSON.parse(contents.toString())));
						cb(err);
					});
				}
				var interval = spinner();
				fs.readFile(path.join(sdk.path,'api.jsca'), function(err,buf){
					if (err) {
						clearInterval(interval);
						logger.warn('warn: could not find api.jsca file, auto-complete disabled');
						return cb();
					}
					var tabCompletionDB = [];
					JSON.parse(buf.toString()).types.forEach(function(entry){
						tabCompletionDB.push(entry.name);
						entry.properties && entry.properties.forEach(function(prop){
							tabCompletionDB.push(entry.name+'.'+prop.name);
						});
						entry.functions && entry.functions.forEach(function(fn){
							tabCompletionDB.push(entry.name+'.'+fn.name);
						});
					});
					fs.writeFile(cacheFile, JSON.stringify(tabCompletionDB), function(err){
						clearInterval(interval);
						tabCompletionSetup(tabCompletionDB);
						cb(err);
					});
				});
			});
		},

		// create the repl project
		function(cb) {
			if (opts.clean) {
				fs.removeSync(TRIPLE_APP);
			}

			if (!fs.existsSync(TRIPLE_APP)) {
				logger.system('creating app');
				var interval = spinner();
				var createOpts = _.defaults(opts.create || {}, {
					name: 'app',
					id: 'triple.tmpapp',
					workspaceDir: TRIPLE_DIR,
					platform: ['android','ios'],
					logLevel: 'error'
				});
				titanium.create(createOpts, function(err, results) {
					clearInterval(interval);
					if (err && !opts.verbose) {
						return cb(new Error('create failed: try with --verbose for more information.'));
					}
					cb(err, results);
				});
			} else {
				cb();
			}
		},

		// prep app
		function(cb) {
			var resources = path.join(TRIPLE_APP, 'Resources'),
				app = path.join(__dirname, '..', 'app');

			// copy in all app files
			fs.readdirSync(app).forEach(function(file) {
				copy(path.join(app, file), path.join(resources, file));
			});

			// copy in shared constants file
			copy(path.join(__dirname, 'constants.js'), path.join(resources, 'constants.js'));

			// load tiapp.xml
			var tiapp = tiappXml.load(path.resolve(TRIPLE_APP, 'tiapp.xml'));

			// disable analytics
			tiapp.analytics = false;

			// load native modules into tiapp.xml
			if(opts.module) {
				opts.module.forEach(function(id) {
					logger.system('Injecting module: %s', id);
					tiapp.setModule(id);
				});
			}

			tiapp.write();

			cb();
		},

		// // start up repl server
		function(cb) {
			server.on('end', function(data) {
				return cb(new Error(data));
			});
			server.listen(constants.PORT, cb);
		},

		// start up file server
		function(cb) {
			//if (opts.platform === 'ios') { return cb(); }
			fileServer.on('end', function(data) {
				return cb(new Error(data));
			});
			fileServer.listen(constants.PORT_FILE, cb);
		},

		// build the repl project
		function(cb) {
			logger.system('launching app');
			var interval = spinner();
			var buildOpts = _.defaults(opts.build || {}, {
				projectDir: TRIPLE_APP,
				platform: opts.platform,
				server: server
			});
			if (opts.platform === 'ios') { buildOpts.noSimFocus = true; }

			titanium.build(buildOpts, function(err) {
				if (err) {
					if (!opts.verbose) {
						return cb(new Error('build failed: try with --verbose for more information.'));
					} else {
						return cb(err);
					}
				}
			});
			server.on('ready', function() {
				clearInterval(interval);
				cb();
			});
		},

		// prompt
		function(cb) {

			// clear spinner and prompt
			process.stdout.write('\r \r');
			rl.setPrompt(constants.PROMPT);
			rl.prompt();

			// throttle incoming code
			var queue = [],
				interval;
			rl.on('line', function(line) {
				interval && clearTimeout(interval);
				queue.push(line);
				interval = setTimeout(processQueue, 100);
			});

			// handle each line of input
			function processQueue() {

				// turn queued lines into a single message
				var match;
				var line = queue.join('\n');
				queue = [];

				// move cursor to next line if necessary
				if (rl.cursor) {
					process.stdout.write('\n');
				}

				// skip empty lines
				if (!line) {
					rl.prompt();
					return;

				// handle triple commands
				} else if (match = line.match(/^\s*(\.\w*)(?:\s|$)/)) {
					var args = match.input.trim().split(/\s+/).slice(1);

					// don't put triple commands on history
					rl.history.shift();

					// execute the command
					switch(match[1].substring(1)) {
						case 'add':
							for (var i = 0; i < args.length; i++) {
								util.addToApp(args[i]);
							}
							break;
						case 'break':
							buffer = [];
							rl.setPrompt(constants.PROMPT);
							break;
						case 'clear':
							server.emit('message', constants.CLEAR_MESSAGE);
							server.resetInterval = spinner();
							return; // wait for context reset message from app before prompting again
						case 'help':
							util.printCommands(0);
							break;
						case 'load':
							load(args, rl);
							break;
						case 'exit':
							return cb();
						case 'save':
							var file = args[0];
							if (!file) {
								logger.error('must specify path for save');
							} else {
								fs.ensureDir(path.dirname(file));
								fs.writeFileSync(file, rl.history.slice(0).reverse().join('\n'));
							}
							break;
						default:
							logger.error('invalid command "' + match[1] + '"');
							break;
					}
					rl.prompt();

				// process code
				} else {
					try {

						var origLine = line;

						// update paths to file names
						if (opts.platform !== 'ios' && util.fileRx) {
							line = line.replace(util.fileRx, 'Ti.Filesystem.' + constants.DIR_PROP + '+$1');
						}

						// if it contains a require(), but isn't a native require
						if (opts.platform === 'ios' && (match = line.match(requireRE))
							&& !_.contains(opts.module, match[2]))
						{
							var moduleId = match[2],
								modulePath;

							// get absolute path
							if (moduleId.indexOf('/') === 0) {
								modulePath = moduleId;
							} else {
								modulePath = path.join(process.cwd(), moduleId);
							}

							// .add it
							var addPath = modulePath + (!/\.js$/.test(modulePath) ? '.js' : '');
							util.addToApp(addPath);

							// modify the line before sending it to the app
							line = line.substring(0, match.index) + match[1] + '__modules' + modulePath +
								match[3] + line.substring(match.index + match[0].length);

						}

						// did the line change?
						if (origLine !== line) {
							logger.debug(constants.PROMPT + line);
						}

						// assemble code from buffer
						buffer.push(line);
						var code = buffer.join('\n');

						// validate the buffered code
						acorn.parse(code);

						// send code to server
						server.emit('message', code);

						// reset buffer and prompt
						buffer = [];
						rl.setPrompt(constants.PROMPT);
					} catch (e) {

						// prompt for multi-line statement
						rl.setPrompt(constants.CONTINUE_PROMPT);
						rl.prompt();
					}
				}
			}

			// handle CTRL+C
			rl.on('SIGINT', function() {

				// If we're in a multi-line statement, break out of it
				if (buffer.length) {
					buffer = [];
					logger.log('\n(^C again to quit)');
					rl.setPrompt(constants.PROMPT);
					rl.prompt();

				// quit
				} else {
					cb();
				}
			});

			// do we have a file to load?
			if (opts.args && opts.args.length) {
				load(opts.args, rl);
			}

		}

	], callback);
};

function copy(src, dst) {
	fs.writeFileSync(dst, fs.readFileSync(src));
}

function sendLoadCommand(rl, location, content, delay) {
	try {
		acorn.parse(content);
	} catch (err) {
		logger.error('error parsing "' + location + '"');
		logger.error(err);
		return rl.prompt();
	}

	var tasks = [];
	content.split('\n').forEach(function(line){
		line = line.trim();
		if (line) {
			tasks.push(function(next){
				setTimeout(function(){
					rl.write(line + '\n');
					next();
				}, delay || 25);
			});
		}
	});
	return async.series(tasks);
}

function load(tokens, rl) {
	var location = tokens[0],
		delay = tokens[1] || 0;

	// looks like a url, fetch it
	if (/^https?:\/\//.test(location)) {
		var interval = spinner();
		return request(location, function(err,content) {
			clearInterval(interval);
			if (err) {
				logger.error('error loading "' + location + '"');
				logger.error(err);
				rl.prompt();
				return;
			}
			sendLoadCommand(rl, location, content.body, delay);
		});

	// probably a file
	} else {
		var fullpath = path.resolve(process.cwd(), location);
		if (fs.existsSync(fullpath)) {

			// if it's a folder
			if (fs.lstatSync(fullpath).isDirectory()) {

				// try app.js and/or index.js
				var tries = ['app.js', 'index.js'];
				for (var i = 0; i < tries.length; i++) {
					var file = path.join(fullpath, tries[i]);
					if (fs.existsSync(file)) {
						return sendLoadCommand(rl, file, fs.readFileSync(file, 'utf8'), delay);
					}
				}
			}

			// if it's a file
			else {
				return sendLoadCommand(rl, fullpath, fs.readFileSync(fullpath, 'utf8'), delay);
			}
		}
	}

	logger.error('not sure how to load "' + location + '". does it exist?');
	rl.prompt();
}

function tabCompletionSetup(tabCompletionDB) {
	if (tabCompletionDB && tabCompletionDB.length) {
		tabCompletionDB.forEach(function(name){
			var tokens = name.split('.'),
				obj = tabCompletionNS;
			for (var c=0;c<tokens.length;c++){
				var cname = tokens[c];
				var entry = obj[cname];
				if (!entry) {
					obj[cname] = entry = {};
				}
				obj = entry;
			}
		});
		tabCompletionDB = null;
		tabCompletionNS.Ti = tabCompletionNS.Titanium;
	}
}

function tabCompletion(rl) {
	return function(line) {

		var match = line.match(simpleExpressionRE);
		if (!match) { return [[],line]; }
		line = match[0];

		var tokens = line.split('.'),
			obj = tabCompletionNS,
			hits = [];

		for (var c=0;c<tokens.length;c++) {
			var token = tokens[c];
			if (token) {
				var entry = obj[token];
				if (!entry) {
					hits = Object.keys(obj).filter(function(e){
						return e.indexOf(token)===0;
					});
					obj = null;
					line = token;
					break;
				}
				obj = entry;
			}
		}

		if (obj) {
			hits = Object.keys(obj);
		}

		return [!hits.length && !line ? Object.keys(tabCompletionNS) : hits, line];
	};
}
<|endoftext|>


<commit_before>require('dotenv').load();
var client = require('twilio')(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN),
    clc = require('cli-color');

var sendSMS = function(status, versionNumber) {
  client.sendMessage({

    to: process.env.PHONE_NUMBER, // Any number Twilio can deliver to
    from: process.env.TWILIO_NUMBER, // A number you bought from Twilio and can use for outbound communication
    body: "There is a new " + status + " version of io.js, version: " + versionNumber

  }, function(err, responseData) {
    if (!err) {
      console.log(clc.blue("Message sent successfully!"));
      console.log(clc.white(responseData.body));
    }
  });
};

module.exports = sendSMS;
<commit_msg>log error to console<commit_after>require('dotenv').load();
var client = require('twilio')(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN),
    clc = require('cli-color');

var sendSMS = function(status, versionNumber) {
  client.sendMessage({

    to: process.env.PHONE_NUMBER, // Any number Twilio can deliver to
    from: process.env.TWILIO_NUMBER, // A number you bought from Twilio and can use for outbound communication
    body: "There is a new " + status + " version of io.js, version: " + versionNumber

  }, function(err, responseData) {
    if (!err) {
      console.log(clc.blue("Message sent successfully!"));
      console.log(clc.white(responseData.body));
    } else {
    console.error(clc.red(err));
  }
  });
};

module.exports = sendSMS;
<|endoftext|>