<commit_before>require([
    // AppConfig
    "config/defaults",

    // Map
    "esri/arcgis/utils",

    // Time Animation
    "esri/TimeExtent",

    // Chart
    "app/d3ScatterPlot",
    "esri/tasks/query",
    "esri/tasks/QueryTask",

    // Legend
    "esri/dijit/Legend",

    // Dojo Utils
    "dojo/dom-style",
    "dojo/dom-attr",
    "dojo/dom",
    "dojo/on",
    "dojo/query",
    "dojo/_base/array",

    "dojo/domReady!"
], function (
    appConfig,

	arcgisUtils,

    TimeExtent,

    Chart,
    Query,
    QueryTask,

    Legend,

	domStyle,
	domAttr,
	dom,
	on,
	dojoQuery,
	arrayUtils
  ){

    var map, eq_depth, eq_magnitude, playInterval = 6200;
    var currentTime = new Date();
    var today = new Date((currentTime.getMonth() + 1) + "/" + currentTime.getDate() + "/" + currentTime.getFullYear() + " UTC");
    var y = currentTime.getFullYear();
    var m = currentTime.getMonth() - 1;
    var d = currentTime.getDate();
    var now = { start: new Date(m + "/1/" + y + " UTC"), end: new Date(m + "/2/" + y + " UTC") };
    var threeMonthsExtent = { start: new Date(m + "/1/" + y + " UTC"), end: new Date((m+3) + "/1/" + y + " UTC") };
    var chart, eq_allData = [], nextStartId = 0;
    var eq_queryTask = new QueryTask(appConfig.chart.dataUrl);
    console.log(today, now.end);

    // マップ作成
    function initMap() {
        var deferred = arcgisUtils.createMap(appConfig.map.id, "mapView", {
            mapOptions: {
                sliderPosition: "bottom-right"
            }
        }).then(function(response) {
            map = response.map;
            map.disableMapNavigation(); // 地図操作無効化
            console.log(map);

            dom.byId("viz-title").innerHTML = response.itemInfo.item.title;

            var legend = new Legend({
				map: map
			}, "mapLegendView");
			legend.startup();

            //initTime();
            getEQAllData();

            eq_depth = map.getLayer(appConfig.layer.eq_depth);
            eq_magnitude = map.getLayer(appConfig.layer.eq_magnitude);

            eq_magnitude.on("graphic-draw", eq_magnitude_draw);
            eq_magnitude.on("mouse-over", eq_magnitude_hover);
            eq_magnitude.on("mouse-out", eq_magnitude_mouseout);
        });
    }

    // チャート作成
    function initChart(data) {
        chart = new Chart({
            map: map,
            layer: eq_magnitude,
            data: data
        }, "chartView");
        setTimeout(function() {
            initTime();
        }, 6200);
    }

    // チャートハイライトレンジ
    function updateChart() {
        chart.setHighlightSpan(now.start, now.end);
    }

    // 地震全データ取得クエリ（チャート用）
    function getEQAllData(startId) {
        var query = new Query();
        query.outFields = ["OBJECTID", "MAGNITUDE", "DEPTH", "UTC_DATETIME"];
        query.returnGeometry = false;
        //query.where = "1=1";
        var timeExtent = new TimeExtent();
        timeExtent.startTime = threeMonthsExtent.start;
        timeExtent.endTime = threeMonthsExtent.end;
        query.timeExtent = timeExtent;
        if(startId !== undefined) {
            query.where = "MAGNITUDE >= 4 AND OBJECTID > " + startId;
        }
        else {
            query.where = "MAGNITUDE >= 4";
        }

        eq_queryTask.execute(query, successGetEQAllData, errorGetEQAllData);
    }

    // 地震全データ取得コールバック（チャート用）
    function successGetEQAllData(e) {
        console.log("successGetEQAllData", e);
        arrayUtils.forEach(e.features, function(f) {
            if(f.attributes["UTC_DATETIME"] != null || f.attributes["UTC_DATETIME"] != undefined) {
                eq_allData.push(f.attributes);
            }
        });

        if(e.exceededTransferLimit === true) {
            nextStartId += 1000;
            getEQAllData(nextStartId);
        }
        else {
            console.log("finish: successGetEQAllData", eq_allData);
            initChart(eq_allData);
        }
    }

    // 地震全データ取得エラー（チャート用）
    function errorGetEQAllData() {
        console.log("errorGetEQAllData");
    }

    // 時間の初期化（更新）
    function initTime() {
        dom.byId("now").innerHTML = "" + now.start.getFullYear() + "/" + (now.start.getMonth() + 1) + "/" + now.start.getDate() + "";
        var timeExtent = new TimeExtent();
        timeExtent.startTime = now.start;
        timeExtent.endTime = now.end;
        map.setTimeExtent(timeExtent);
        console.log(timeExtent);

        updateChart();

        setTimeout(function() {
            forwardTime();
        }, playInterval);
    }

    // １日進める OR 初期に戻る
    function forwardTime() {
        if(now.start >= today) {
            now = { start: new Date(m + "/1/" + y + " UTC"), end: new Date(m + "/2/" + y + " UTC") };
        }
        else {
            now.start.setDate(now.start.getDate() + 1);
            now.end.setDate(now.end.getDate() + 1);
        }
        initTime();
    }

    // 地震レイヤーの描画エフェクト
    function eq_magnitude_draw(e) {
        //console.log(e.node);
        //console.log(e.graphic._shape.shape.r);
        d3.select(e.node).attr("r", 0);
        d3.select(e.node).transition()
            .delay(0)
            .duration(4000)
            .attr("r", e.graphic._shape.shape.r);
    }

    //
    function eq_magnitude_hover(e) {

    }

    //
    function eq_magnitude_mouseout(e) {

    }

    initMap();
});<commit_msg>updates: earthquake<commit_after>require([
    // AppConfig
    "config/defaults",

    // Map
    "esri/arcgis/utils",

    // Time Animation
    "esri/TimeExtent",

    // Chart
    "app/d3ScatterPlot",
    "esri/tasks/query",
    "esri/tasks/QueryTask",

    // Legend
    "esri/dijit/Legend",

    // Dojo Utils
    "dojo/dom-style",
    "dojo/dom-attr",
    "dojo/dom",
    "dojo/on",
    "dojo/query",
    "dojo/_base/array",

    "dojo/domReady!"
], function (
    appConfig,

	arcgisUtils,

    TimeExtent,

    Chart,
    Query,
    QueryTask,

    Legend,

	domStyle,
	domAttr,
	dom,
	on,
	dojoQuery,
	arrayUtils
  ){

    var map, eq_depth, eq_magnitude, playInterval = 6200;
    var currentTime = new Date();
    //var today = new Date((currentTime.getMonth() + 1) + "/" + currentTime.getDate() + "/" + currentTime.getFullYear() + " UTC");
    var today = new Date("4/4/2016 UTC"); // USGS Live Feed のデータが現在この日までで終わってる
    var y = currentTime.getFullYear();
    var m = currentTime.getMonth() - 1;
    var d = currentTime.getDate();
    var now = { start: new Date(m + "/1/" + y + " UTC"), end: new Date(m + "/2/" + y + " UTC") };
    var threeMonthsExtent = { start: new Date(m + "/1/" + y + " UTC"), end: new Date((m+3) + "/1/" + y + " UTC") };
    var chart, eq_allData = [], nextStartId = 0;
    var eq_queryTask = new QueryTask(appConfig.chart.dataUrl);
    console.log(today, now.end);

    // マップ作成
    function initMap() {
        var deferred = arcgisUtils.createMap(appConfig.map.id, "mapView", {
            mapOptions: {
                sliderPosition: "bottom-right"
            }
        }).then(function(response) {
            map = response.map;
            map.disableMapNavigation(); // 地図操作無効化
            console.log(map);

            dom.byId("viz-title").innerHTML = response.itemInfo.item.title;

            var legend = new Legend({
				map: map
			}, "mapLegendView");
			legend.startup();

            //initTime();
            getEQAllData();

            eq_depth = map.getLayer(appConfig.layer.eq_depth);
            eq_magnitude = map.getLayer(appConfig.layer.eq_magnitude);

            eq_magnitude.on("graphic-draw", eq_magnitude_draw);
            eq_magnitude.on("mouse-over", eq_magnitude_hover);
            eq_magnitude.on("mouse-out", eq_magnitude_mouseout);
        });
    }

    // チャート作成
    function initChart(data) {
        chart = new Chart({
            map: map,
            layer: eq_magnitude,
            data: data
        }, "chartView");
        setTimeout(function() {
            initTime();
        }, 6200);
    }

    // チャートハイライトレンジ
    function updateChart() {
        chart.setHighlightSpan(now.start, now.end);
    }

    // 地震全データ取得クエリ（チャート用）
    function getEQAllData(startId) {
        var query = new Query();
        query.outFields = ["OBJECTID", "MAGNITUDE", "DEPTH", "UTC_DATETIME"];
        query.returnGeometry = false;
        //query.where = "1=1";
        var timeExtent = new TimeExtent();
        timeExtent.startTime = threeMonthsExtent.start;
        timeExtent.endTime = threeMonthsExtent.end;
        query.timeExtent = timeExtent;
        if(startId !== undefined) {
            query.where = "MAGNITUDE >= 4 AND OBJECTID > " + startId;
        }
        else {
            query.where = "MAGNITUDE >= 4";
        }

        eq_queryTask.execute(query, successGetEQAllData, errorGetEQAllData);
    }

    // 地震全データ取得コールバック（チャート用）
    function successGetEQAllData(e) {
        console.log("successGetEQAllData", e);
        arrayUtils.forEach(e.features, function(f) {
            if(f.attributes["UTC_DATETIME"] != null || f.attributes["UTC_DATETIME"] != undefined) {
                eq_allData.push(f.attributes);
            }
        });

        if(e.exceededTransferLimit === true) {
            nextStartId += 1000;
            getEQAllData(nextStartId);
        }
        else {
            console.log("finish: successGetEQAllData", eq_allData);
            initChart(eq_allData);
        }
    }

    // 地震全データ取得エラー（チャート用）
    function errorGetEQAllData() {
        console.log("errorGetEQAllData");
    }

    // 時間の初期化（更新）
    function initTime() {
        dom.byId("now").innerHTML = "" + now.start.getFullYear() + "/" + (now.start.getMonth() + 1) + "/" + now.start.getDate() + "";
        var timeExtent = new TimeExtent();
        timeExtent.startTime = now.start;
        timeExtent.endTime = now.end;
        map.setTimeExtent(timeExtent);
        console.log(timeExtent);

        updateChart();

        setTimeout(function() {
            forwardTime();
        }, playInterval);
    }

    // １日進める OR 初期に戻る
    function forwardTime() {
        if(now.start >= today) {
            now = { start: new Date(m + "/1/" + y + " UTC"), end: new Date(m + "/2/" + y + " UTC") };
        }
        else {
            now.start.setDate(now.start.getDate() + 1);
            now.end.setDate(now.end.getDate() + 1);
        }
        initTime();
    }

    // 地震レイヤーの描画エフェクト
    function eq_magnitude_draw(e) {
        //console.log(e.node);
        //console.log(e.graphic._shape.shape.r);
        d3.select(e.node).attr("r", 0);
        d3.select(e.node).transition()
            .delay(0)
            .duration(4000)
            .attr("r", e.graphic._shape.shape.r);
    }

    //
    function eq_magnitude_hover(e) {

    }

    //
    function eq_magnitude_mouseout(e) {

    }

    initMap();
});<|endoftext|>


<commit_before>enyo.kind({
	name: "Ares",
	kind: "Control",
	classes: "app onyx",
	fit: true,
	components: [
		{kind: "Panels", /*arrangerKind: "CarouselArranger",*/ classes: "enyo-fit", components: [
			{kind: "Phobos", onSaveDocument: "saveDocument", onCloseDocument: "closeDocument", onDesignDocument: "designDocument", onEditedChanged: "documentEdited"},
			{kind: "Deimos", onCloseDesigner: "closeDesigner"}
		]},
		{kind: "Slideable", style: "height: 100%; width: 100%", layoutKind: "FittableRowsLayout", classes: "onyx", axis: "v", value: 0, min: -500, max: 0, unit: "px", onAnimateFinish: "finishedSliding", components: [
			{kind: "ProjectView", fit: true, classes: "onyx", onFileDblClick: "doubleclickFile"},
			{name: "bottomBar", kind: "DocumentToolbar",
			    onGrabberTap: "toggleFiles",
				onSwitchFile: "switchFile",
				onSave: "bounceSave",
				onDesign: "bounceDesign",
				onNewKind: "bounceNew",
				onClose: "bounceClose"
			}
		]},
		{kind: "ServiceRegistry"}
	],
	handlers: {
		onReloadServices: "handleReloadServices"
	},
	phobosViewIndex: 0,
	deimosViewIndex: 1,
	create: function() {
		this.inherited(arguments);
		this.$.panels.setIndex(this.phobosViewIndex);

		window.onbeforeunload = enyo.bind(this, "handleBeforeUnload");
		if (this.runTest) {
			// in charge of Ares Test Suite when Ares Ide launch with runTest option
			this.createComponent({kind: "ares.TestController"});
		}
		this.calcSlideableLimit();
	},
	rendered: function() {
		this.inherited(arguments);
		this.calcSlideableLimit();
	},
	openFiles: {},
	draggable: false,
	handleReloadServices: function(inSender, inEvent) {
		this.$.serviceRegistry.reloadServices();
	},
	doubleclickFile: function(inSender, inEvent) {
		var f = inEvent.file;
		var d = this.openFiles[inEvent.file.id];
		if (d) {
			this.switchToDocument(d);
		} else {
			this.$.bottomBar.createFileTab(f.name, f.id);
			this.openDocument(inSender, inEvent);
		}
	},
	openDocument: function(inSender, inEvent) {
		var f = inEvent.file;
		var service = f.service;
		var ext = f.name.split(".").pop();
		var origin = service.getConfig().origin;
		var projectUrl = origin + service.getConfig().pathname + "/file" + inEvent.project;
		this.$.phobos.beginOpenDoc();
		service.getFile(f.id)
			.response(this, function(inEvent, inData) {
				if (inData.content) {
					inData=inData.content;
				} else {
					// no data? Empty file
					inData="";
				}
				if (this.openFiles[f.id]) {
					alert("Duplicate File ID in cache!");
				}
				var doc = {
					origin: origin,
					file: f,
					data: inData,
					extension: ext,
					projectUrl: projectUrl,
					edited: false
				};
				this.openFiles[f.id] = doc;
				this.switchToDocument(doc);
			})
			.error(this, function(inEvent, inData) {
				enyo.log("Open failed", inData);
				this.$.phobos.hideWaitPopup();
			});
	},
	saveDocument: function(inSender, inEvent) {
		var service = inEvent.file.service;
		service.putFile(inEvent.file.id, inEvent.content)
			.response(this, function(inEvent, inData) {
				inSender.saveComplete();
				this.$.deimos.saveComplete();
			})
			.error(this, function(inEvent, inData) {
				inSender.saveFailed(inData);
			});
	},
	closeDocument: function(inSender, inEvent) {
		var id = inSender.file.id;
		// remove file from cache
		this.openFiles[id]=undefined;
		this.$.bottomBar.removeTab(id);
		this.showFiles();
	},
	designDocument: function(inSender, inEvent) {
		this.$.deimos.load(inEvent);
		this.$.panels.setIndex(this.deimosViewIndex);
	},
	closeDesigner: function(inSender, inEvent) {
		if (inEvent.docHasChanged) {
			this.$.phobos.updateComponents(inSender, inEvent);
		}
		this.$.panels.setIndex(this.phobosViewIndex);
	},
	handleBeforeUnload: function() {
		if (window.location.search.indexOf("debug") == -1) {
			return 'You may have some unsaved data';
		}
	},
	hideFiles: function(inSender, inEvent) {
		this.$.slideable.animateToMin();
	},
	showFiles: function(inSender, inEvent) {
		this.$.slideable.animateToMax();
	},
	toggleFiles: function(inSender, inEvent) {
		if (this.$.slideable.value < 0) {
			this.showFiles();
		} else {
			this.hideFiles();
		}
	},
	resizeHandler: function(inSender, inEvent) {
		this.inherited(arguments);
		this.calcSlideableLimit();
		if (this.$.slideable.value < 0) {
			this.$.slideable.setValue(this.$.slideable.min);
		}
	},
	calcSlideableLimit: function() {
		var min = this.getBounds().height-this.$.bottomBar.getBounds().height;
		this.$.slideable.setMin(-min);
	},
	switchFile: function(inSender, inEvent) {
		var d = this.openFiles[inEvent.id];
		if (d) {
			this.switchToDocument(d);
		} else {
			alert("File ID not found in cache!");
		}
	},
	switchToDocument: function(d) {
		// save document state
		if (this.activeDocument) {
			this.activeDocument.data = this.$.phobos.getEditorContent();
		}
		if (!this.activeDocument || d !== this.activeDocument) {
			this.$.phobos.openDoc(d.origin, d.file, d.data, d.extension, d.projectUrl, d.edited);
		}
		this.$.panels.setIndex(this.phobosViewIndex);
		this.$.bottomBar.activateFileWithId(d.file.id);
		this.hideFiles();
		this.activeDocument = d;
	},
	finishedSliding: function(inSender, inEvent) {
		if (this.$.slideable.value < 0) {
			this.$.bottomBar.showControls();
		} else {
			this.$.bottomBar.hideControls();
		}
	},
	// FIXME: This trampoline function probably needs some refactoring
	bounceSave: function(inSender, inEvent) {
		this.$.phobos.saveDocAction(inSender, inEvent);
	},
	// FIXME: This trampoline function probably needs some refactoring
	bounceDesign: function(inSender, inEvent) {
		this.$.phobos.designerAction(inSender, inEvent);
	},
	// FIXME: This trampoline function probably needs some refactoring
	bounceNew: function(inSender, inEvent) {
		this.$.phobos.newKindAction(inSender, inEvent);
	},
	// FIXME: This trampoline function probably needs some refactoring
	// Close is a special case, because it can be invoked on a document other than the currently-active one
	bounceClose: function(inSender, inEvent) {
		this.switchFile(inSender, inEvent);
		enyo.asyncMethod(this.$.phobos, "closeDocAction");
	},
	documentEdited: function(inSender, inEvent) {
		var id = inEvent.id;
		if (this.openFiles[id]) {
			this.openFiles[id].edited = inEvent.edited;
		} else {
			alert("File ID not found in cache!");
		}
	}
});
<commit_msg>missed in last commit<commit_after>enyo.kind({
	name: "Ares",
	kind: "Control",
	classes: "app onyx",
	fit: true,
	components: [
		{kind: "Panels", /*arrangerKind: "CarouselArranger",*/ classes: "enyo-fit", components: [
			{kind: "Phobos", onSaveDocument: "saveDocument", onCloseDocument: "closeDocument", onDesignDocument: "designDocument", onEditedChanged: "documentEdited"},
			{kind: "Deimos", onCloseDesigner: "closeDesigner"}
		]},
		{kind: "Slideable", style: "height: 100%; width: 100%", layoutKind: "FittableRowsLayout", classes: "onyx", axis: "v", value: 0, min: -500, max: 0, unit: "px", onAnimateFinish: "finishedSliding", components: [
			{kind: "ProjectView", fit: true, classes: "onyx", onFileDblClick: "doubleclickFile"},
			{name: "bottomBar", kind: "DocumentToolbar",
			    onGrabberTap: "toggleFiles",
				onSwitchFile: "switchFile",
				onSave: "bounceSave",
				onDesign: "bounceDesign",
				onNewKind: "bounceNew",
				onClose: "bounceClose"
			}
		]},
		{kind: "ServiceRegistry"}
	],
	handlers: {
		onReloadServices: "handleReloadServices"
	},
	phobosViewIndex: 0,
	deimosViewIndex: 1,
	create: function() {
		this.inherited(arguments);
		this.$.panels.setIndex(this.phobosViewIndex);

		window.onbeforeunload = enyo.bind(this, "handleBeforeUnload");
		if (this.runTest) {
			// in charge of Ares Test Suite when Ares Ide launch with runTest option
			this.createComponent({kind: "ares.TestController"});
		}
		this.calcSlideableLimit();
	},
	rendered: function() {
		this.inherited(arguments);
		this.calcSlideableLimit();
	},
	openFiles: {},
	draggable: false,
	handleReloadServices: function(inSender, inEvent) {
		this.$.serviceRegistry.reloadServices();
	},
	doubleclickFile: function(inSender, inEvent) {
		var f = inEvent.file;
		var d = this.openFiles[inEvent.file.id];
		if (d) {
			this.switchToDocument(d);
		} else {
			this.$.bottomBar.createFileTab(f.name, f.id);
			this.openDocument(inSender, inEvent);
		}
	},
	openDocument: function(inSender, inEvent) {
		var f = inEvent.file;
		var service = f.service;
		var ext = f.name.split(".").pop();
		var origin = service.getConfig().origin;
		var projectUrl = origin + service.getConfig().pathname + "/file" + inEvent.projectPath;
		this.$.phobos.beginOpenDoc();
		service.getFile(f.id)
			.response(this, function(inEvent, inData) {
				if (inData.content) {
					inData=inData.content;
				} else {
					// no data? Empty file
					inData="";
				}
				if (this.openFiles[f.id]) {
					alert("Duplicate File ID in cache!");
				}
				var doc = {
					origin: origin,
					file: f,
					data: inData,
					extension: ext,
					projectUrl: projectUrl,
					edited: false
				};
				this.openFiles[f.id] = doc;
				this.switchToDocument(doc);
			})
			.error(this, function(inEvent, inData) {
				enyo.log("Open failed", inData);
				this.$.phobos.hideWaitPopup();
			});
	},
	saveDocument: function(inSender, inEvent) {
		var service = inEvent.file.service;
		service.putFile(inEvent.file.id, inEvent.content)
			.response(this, function(inEvent, inData) {
				inSender.saveComplete();
				this.$.deimos.saveComplete();
			})
			.error(this, function(inEvent, inData) {
				inSender.saveFailed(inData);
			});
	},
	closeDocument: function(inSender, inEvent) {
		var id = inSender.file.id;
		// remove file from cache
		this.openFiles[id]=undefined;
		this.$.bottomBar.removeTab(id);
		this.showFiles();
	},
	designDocument: function(inSender, inEvent) {
		this.$.deimos.load(inEvent);
		this.$.panels.setIndex(this.deimosViewIndex);
	},
	closeDesigner: function(inSender, inEvent) {
		if (inEvent.docHasChanged) {
			this.$.phobos.updateComponents(inSender, inEvent);
		}
		this.$.panels.setIndex(this.phobosViewIndex);
	},
	handleBeforeUnload: function() {
		if (window.location.search.indexOf("debug") == -1) {
			return 'You may have some unsaved data';
		}
	},
	hideFiles: function(inSender, inEvent) {
		this.$.slideable.animateToMin();
	},
	showFiles: function(inSender, inEvent) {
		this.$.slideable.animateToMax();
	},
	toggleFiles: function(inSender, inEvent) {
		if (this.$.slideable.value < 0) {
			this.showFiles();
		} else {
			this.hideFiles();
		}
	},
	resizeHandler: function(inSender, inEvent) {
		this.inherited(arguments);
		this.calcSlideableLimit();
		if (this.$.slideable.value < 0) {
			this.$.slideable.setValue(this.$.slideable.min);
		}
	},
	calcSlideableLimit: function() {
		var min = this.getBounds().height-this.$.bottomBar.getBounds().height;
		this.$.slideable.setMin(-min);
	},
	switchFile: function(inSender, inEvent) {
		var d = this.openFiles[inEvent.id];
		if (d) {
			this.switchToDocument(d);
		} else {
			alert("File ID not found in cache!");
		}
	},
	switchToDocument: function(d) {
		// save document state
		if (this.activeDocument) {
			this.activeDocument.data = this.$.phobos.getEditorContent();
		}
		if (!this.activeDocument || d !== this.activeDocument) {
			this.$.phobos.openDoc(d.origin, d.file, d.data, d.extension, d.projectUrl, d.edited);
		}
		this.$.panels.setIndex(this.phobosViewIndex);
		this.$.bottomBar.activateFileWithId(d.file.id);
		this.hideFiles();
		this.activeDocument = d;
	},
	finishedSliding: function(inSender, inEvent) {
		if (this.$.slideable.value < 0) {
			this.$.bottomBar.showControls();
		} else {
			this.$.bottomBar.hideControls();
		}
	},
	// FIXME: This trampoline function probably needs some refactoring
	bounceSave: function(inSender, inEvent) {
		this.$.phobos.saveDocAction(inSender, inEvent);
	},
	// FIXME: This trampoline function probably needs some refactoring
	bounceDesign: function(inSender, inEvent) {
		this.$.phobos.designerAction(inSender, inEvent);
	},
	// FIXME: This trampoline function probably needs some refactoring
	bounceNew: function(inSender, inEvent) {
		this.$.phobos.newKindAction(inSender, inEvent);
	},
	// FIXME: This trampoline function probably needs some refactoring
	// Close is a special case, because it can be invoked on a document other than the currently-active one
	bounceClose: function(inSender, inEvent) {
		this.switchFile(inSender, inEvent);
		enyo.asyncMethod(this.$.phobos, "closeDocAction");
	},
	documentEdited: function(inSender, inEvent) {
		var id = inEvent.id;
		if (this.openFiles[id]) {
			this.openFiles[id].edited = inEvent.edited;
		} else {
			alert("File ID not found in cache!");
		}
	}
});
<|endoftext|>


<commit_before>import ns from 'imajs/client/core/namespace';

ns.namespace('Core.Router');

/**
 * Regular expression matching all control characters used in regular
 * expressions. The regular expression is used to match these characters in
 * path expressions and replace them appropriately so the path expression can
 * be compiled to a regular expression.
 *
 * @const
 * @property
 * @type {RegExp}
 */
const CONTROL_CHARACTERS_REGEXP = /[\\.+*?\^$\[\](){}\/\'#]/g;

/**
 * Regular expression used to match and remove the starting and trailing
 * forward slashes from a path expression or a URL path.
 *
 * @const
 * @property
 * @type {RegExp}
 */
const LOOSE_SLASHES_REGEXP = /^\/|\/$/g;

/**
 * Regular expression used to match the parameter names from a path expression.
 *
 * @const
 * @property PARAMS_REGEXP
 * @type {RegExp}
 */
const PARAMS_REGEXP_UNIVERSAL = /:\??([a-zA-Z0-9_-]+)/g;

/**
 * Regular expression used to match the parameter names from a path expression.
 *
 * @const
 * @property PARAMS_REGEXP
 * @type {RegExp}
 */
const PARAMS_REGEXP = /:([a-zA-Z0-9_-]+)/g;

/**
 * Regular expression used to match the parameter names from a path expression.
 *
 * @const
 * @property PARAMS_REGEXP
 * @type {RegExp}
 */
const PARAMS_REGEXP_OPT = /(?:^:\\\?([a-zA-Z0-9_-]+)(\\\/)?)|(?:(\\\/):\\\?([a-zA-Z0-9_-]+))|(?::\\\?([a-zA-Z0-9_-]+))/g;

/**
 * Utility for representing and manipulating a single route in the router's
 * configuration.
 *
 * @class Route
 * @namespace Core.Router
 * @module Core
 * @submodule Core.Router
 */
export default class Route {

	/**
	 * Initializes the route.
	 *
	 * @constructor
	 * @method constructor
	 * @param {string} name The unique name of this route, identifying it among
	 *        the rest of the routes in the application.
	 * @param {string} pathExpression A path expression specifying the URL path
	 *        part matching this route (must not contain a query string),
	 *        optionally containing named parameter placeholders specified as
	 *        {@code :parameterName}.
	 * @param {string} controller The full name of Object Container alias
	 *        identifying the controller associated with this route.
	 * @param {string} view The full name or Object Container alias identifying
	 *        the view class associated with this route.
	 * @param {{onlyUpdate: (boolean|function), autoScroll: boolean}} [options]
	 *        The route additional options.
	 */
	constructor(name, pathExpression, controller, view, options) {
		/**
		 * The unique name of this route, identifying it among the rest of the
		 * routes in the application.
		 *
		 * @private
		 * @property _name
		 * @type {string}
		 */
		this._name = name;

		/**
		 * The original URL path expression from which this route was created.
		 *
		 * @private
		 * @property _pathExpression
		 * @type {string}
		 */
		this._pathExpression = pathExpression;

		/**
		 * The full name of Object Container alias identifying the controller
		 * associated with this route.
		 *
		 * @private
		 * @property _controller
		 * @type {string}
		 */
		this._controller = controller;

		/**
		 * The full name or Object Container alias identifying the view class
		 * associated with this route.
		 *
		 * @private
		 * @property _view
		 * @type {Vendor.React.Component}
		 */
		this._view = view;

		/**
		 * The route additional options.
		 *
		 * @private
		 * @property _options
		 * @type {{onlyUpdate: boolean, autoScroll: boolean}}
		 * @default {onlyUpdate: false, autoScroll: true}
		 */
		this._options = Object.assign(
			{ onlyUpdate: false, autoScroll: true },
			options
		);

		/**
		 * The path expression with the trailing slashes trimmed.
		 *
		 * @private
		 * @property _trimmedPathExpression
		 * @type {string}
		 */
		this._trimmedPathExpression = this._getTrimmedPath(pathExpression);

		/**
		 * The names of the parameters in this route.
		 *
		 * @private
		 * @property _parameterNames
		 * @type {string[]}
		 */
		this._parameterNames = this._getParameterNames(pathExpression);

		/**
		 * Set to {@code true} if this route contains parameters in its path.
		 *
		 * @private
		 * @property _hasParameters
		 * @type {boolean}
		 */
		this._hasParameters = !!this._parameterNames.length;

		/**
		 * A regexp used to match URL path against this route and extract the
		 * parameter values from the matched URL paths.
		 *
		 * @private
		 * @property _matcher
		 * @type {RegExp}
		 */
		this._matcher = this._compileToRegExp(this._trimmedPathExpression);
	}

	/**
	 * Creates the URL and query parts of a URL by substituting the route's
	 * parameter placeholders by the provided parameter value.
	 *
	 * The extraneous parameters that do not match any of the route's
	 * placeholders will be appended as the query string.
	 *
	 * @method toPath
	 * @param {Object<string, (number|string)>} [params={}] The route parameter
	 *        values.
	 * @return {string} Path and, if neccessary, query parts of the URL
	 *         representing this route with its parameters replaced by the
	 *         provided parameter values.
	 */
	toPath(params = {}) {
		var path = this._pathExpression;
		var query = [];
		for (var paramName of Object.keys(params)) {
			if (this._isParamInPath(path, paramName)) {
				path = this._substituteParamInPath(path, paramName, params[paramName]);
				path = this._substituteOptionalParamInPath(path, paramName, params[paramName]);
			} else {
				var pair = [paramName, params[paramName]];
				query.push(pair.map(encodeURIComponent).join('='));
			}
		}
		path = this._cleanUnusedOptionalParams(path);

		path = query.length ? path + '?' + query.join('&') : path;
		path = this._getTrimmedPath(path);
		return path;
	}

	/**
	 *  Replace param placeholder in path with parameter value.
	 *
	 * @method _cleanUnusedOptionalParams
	 * @private
	 * @param {string} path
	 * @param {string} paramName
	 * @param {string} paramValue
	 * @return {string} New path.
	 */
	_substituteParamInPath(path, paramName, paramValue) {
		return path.replace(new RegExp(`(^|\/):${paramName}([\/\?]|$)`), paramValue ? '$1' + paramValue +  '$2' : '');
	}

	/**
	 * Replace optional param placeholder in path with parameter value.
	 *
	 * @method _cleanUnusedOptionalParams
	 * @private
	 * @param {string} path
	 * @param {string} paramName
	 * @param {string} paramValue
	 * @return {string} New path.
	 */
	_substituteOptionalParamInPath(path, paramName, paramValue) {
		return path.replace(new RegExp(`(^|\/):\\\?${paramName}([\/\?]|$)`), paramValue ? '$1' + paramValue +  '$2' : '');
	}

	/**
	 * Replace optional param placeholder in path with parameter value.
	 *
	 * @method _cleanUnusedOptionalParams
	 * @private
	 * @param {string} path
	 * @param {string} paramName
	 * @param {string} paramValue
	 * @return {string} New path.
	 */
	_cleanUnusedOptionalParams(path) {
		return path.replace(/(\/:\?([a-zA-Z0-9_-]+))|(:\?([a-zA-Z0-9_-]+)\/?)/g, '');
	}

	/**
	 * Returns true, if paramName is placed in path.
	 *
	 * @method _isParamInPath
	 * @private
	 * @param {string} path
	 * @param {string} paramName
	 * @return {true}
	 */
	_isParamInPath(path, paramName) {
		var regexp = new RegExp(`:\??${paramName}(?:[\/\?]|$)`);
		return regexp.test(path);
	}

	/**
	 * Returns the unique identifying name of this route.
	 *
	 * @method getName
	 * @return {string} The name of the route, identifying it.
	 */
	getName() {
		return this._name;
	}

	/**
	 * Returns the full name of the controller to use when this route is
	 * matched by the current URL, or an Object Container-registered alias of
	 * the controller.
	 *
	 * @method getController
	 * @return {string} The name of alias of the controller.
	 */
	getController() {
		return this._controller;
	}

	/**
	 * Returns the full name of the view class or an Object
	 * Container-registered alias for the view class, representing the view to
	 * use when this route is matched by the current URL.
	 *
	 * @method getView
	 * @return {string} The name or alias of the view class.
	 */
	getView() {
		return this._view;
	}

	/**
	 * Return route additional options.
	 *
	 * @method getOptions
	 * @return {{onlyUpdate: (boolean|function), autoScroll: boolean}}
	 */
	getOptions() {
		return this._options;
	}

	/**
	 * Returns the path expression, which is the parametrized pattern matching
	 * the URL paths matched by this route.
	 *
	 * @method getPathExpression
	 * @return {string} The path expression.
	 */
	getPathExpression() {
		return this._pathExpression;
	}

	/**
	 * Tests whether the provided URL path matches this route. The provided
	 * path may contain the query.
	 *
	 * @method matches
	 * @param {string} path The URL path.
	 * @return {boolean} {@code true} if the provided path matches this route.
	 */
	matches(path) {
		var trimmedPath = this._getTrimmedPath(path);
		return this._matcher.test(trimmedPath);
	}

	/**
	 * Extracts the parameter values from the provided path. The method
	 * extracts both the in-path parameters and parses the query, allowing the
	 * query parameters to override the in-path parameters.
	 *
	 * The method returns an empty hash object if the path does not match this
	 * route.
	 *
	 * @method extractParameters
	 * @param {string} path
	 * @return {Object<string, ?string>} Map of parameter names to parameter
	 *         values.
	 */
	extractParameters(path) {
		var trimmedPath = this._getTrimmedPath(path);
		var parameters = this._getParameters(trimmedPath);
		var query = this._getQuery(trimmedPath);

		return Object.assign({}, parameters, query);
	}

	/**
	 * Compiles the path expression to a regular expression that can be used
	 * for easier matching of URL paths against this route, and extracting the
	 * path parameter values from the URL path.
	 *
	 * @private
	 * @method _compileToRegExp
	 * @param {string} pathExpression The path expression to compile.
	 * @return {RegExp} The compiled regular expression.
	 */
	_compileToRegExp(pathExpression) {
		var pattern = pathExpression
			.replace(LOOSE_SLASHES_REGEXP, '')
			.replace(CONTROL_CHARACTERS_REGEXP, '\\$&');

		// convert parameters to capture sequences
		pattern = pattern.replace(PARAMS_REGEXP_OPT, '(?:$3([^/?]+)$2)?');

		// convert parameters to capture sequences
		pattern = pattern.replace(PARAMS_REGEXP, '([^/?]+)');

		// add path root
		pattern = '^\\\/' + pattern;

		// add query parameters matcher
		var pairPattern = '[^=&;]*(?:=[^&;]*)?';
		pattern += `(?:\\?(?:${pairPattern})(?:[&;]${pairPattern})*)?$`;

		return new RegExp(pattern);
	}

	/**
	 * Parses the provided path and extract the in-path parameters. The method
	 * decodes the parameters and returns them in a hash object.
	 *
	 * @private
	 * @method _getParameters
	 * @param {string} path The URL path.
	 * @return {Object<string, string>} The parsed path parameters.
	 */
	_getParameters(path) {
		if (!this._hasParameters) {
			return {};
		}

		var parameterValues = path.match(this._matcher);
		if (!parameterValues) {
			return {};
		}

		parameterValues.shift(); // remove the match on whole path, and other parts

		return this._extractParameters(parameterValues);
	}

	_extractParameters(parameterValues) {
		var parameters = {};

		// Find shorter lenght of param values vs. names
		var pathParameterValuesCount = (parameterValues.length - 1 <= i) ? parameterValues.length : this._parameterNames.length;

		// Cycle for names and values from last to 0
		for (var i = this._parameterNames.length - 1, j = pathParameterValuesCount - 1; i >= 0; i--) {

			if (!(this._isParamOptional(this._parameterNames[i])) || (i <= j)) {
				parameters[this._cleanOptParamName(this._parameterNames[i])] = this._decodeURIParameter(parameterValues[j]);
				j--;
			} else {
				parameters[this._cleanOptParamName(this._parameterNames[i])] = undefined;
			}
		}

		return parameters;
	}

	/**
	 * Decoding parameters.
	 *
	 * @method _decodeURIParameter
	 * @param {string} parameterValue
	 * @return {string} decodedValue
	 */
	_decodeURIParameter(parameterValue) {
		var decodedValue;
		if (parameterValue) {
			decodedValue = decodeURIComponent(parameterValue);
		}
		return decodedValue;
	}

	/**
	 * Returns optional param name without "?"
	 *
	 * @method _cleanOptParamName
	 * @param {string} paramName Full param name with "?"
	 * @return {string} Strict param name without "?"
	 */
	_cleanOptParamName(paramName) {
		return paramName.replace('?', '');
	}

	/**
	 * Checks if parameter is optional or not.
	 *
	 * @method _isParamOptional
	 * @param {string} paramName
	 * @return {boolean} return true if is optional, otherwise false
	 */
	_isParamOptional(paramName) {
		return /\?.+/.test(paramName);
	}

	/**
	 * Extracts and decodes the query parameters from the provided URL path and
	 * query.
	 *
	 * @private
	 * @method _getQuery
	 * @param {string} path The URL path, including the optional query string
	 *        (if any).
	 * @return {Object<string, ?string>} Parsed query parameters.
	 */
	_getQuery(path) {
		var query = {};
		var queryStart = path.indexOf('?');
		var hasQuery = queryStart > -1 && queryStart !== path.length - 1;

		if (hasQuery) {
			var pairs = path.substring(queryStart + 1).split(/[&;]/);

			for (var parameterPair of pairs) {
				var pair = parameterPair.split('=');
				query[decodeURIComponent(pair[0])] =
					pair.length > 1 ? decodeURIComponent(pair[1]) : true;
			}
		}

		return query;
	}

	/**
	 * Trims the trailing forward slash from the provided URL path.
	 *
	 * @private
	 * @method getLoosesPath
	 * @param {string} path The path to trim.
	 * @return {string} Trimmed path.
	 */
	_getTrimmedPath(path) {
		return `/${path.replace(LOOSE_SLASHES_REGEXP, '')}`;
	}

	/**
	 * Extracts the parameter names from the provided path expression.
	 *
	 * @private
	 * @method _getParameterNames
	 * @param {string} pathExpression The path expression.
	 * @return {string[]} The names of the parameters defined in the provided
	 *         path expression.
	 */
	_getParameterNames(pathExpression) {
		var rawNames = pathExpression.match(PARAMS_REGEXP_UNIVERSAL) || [];

		return rawNames.map(rawParameterName => rawParameterName.substring(1).replace('?', ''));
	}
}

ns.Core.Router.Route = Route;
<commit_msg>#! Refactoring<commit_after>import ns from 'imajs/client/core/namespace';

ns.namespace('Core.Router');

/**
 * Regular expression matching all control characters used in regular
 * expressions. The regular expression is used to match these characters in
 * path expressions and replace them appropriately so the path expression can
 * be compiled to a regular expression.
 *
 * @const
 * @property
 * @type {RegExp}
 */
const CONTROL_CHARACTERS_REGEXP = /[\\.+*?\^$\[\](){}\/\'#]/g;

/**
 * Regular expression used to match and remove the starting and trailing
 * forward slashes from a path expression or a URL path.
 *
 * @const
 * @property
 * @type {RegExp}
 */
const LOOSE_SLASHES_REGEXP = /^\/|\/$/g;

/**
 * Regular expression used to match the parameter names from a path expression.
 *
 * @const
 * @property PARAMS_REGEXP
 * @type {RegExp}
 */
const PARAMS_REGEXP_UNIVERSAL = /:\??([a-zA-Z0-9_-]+)/g;

/**
 * Regular expression used to match the parameter names from a path expression.
 *
 * @const
 * @property PARAMS_REGEXP
 * @type {RegExp}
 */
const PARAMS_REGEXP = /:([a-zA-Z0-9_-]+)/g;

/**
 * Regular expression used to match the parameter names from a path expression.
 *
 * @const
 * @property PARAMS_REGEXP
 * @type {RegExp}
 */
const PARAMS_REGEXP_OPT = /(?:^:\\\?([a-zA-Z0-9_-]+)(\\\/)?)|(?:(\\\/):\\\?([a-zA-Z0-9_-]+))|(?::\\\?([a-zA-Z0-9_-]+))/g;

/**
 * Utility for representing and manipulating a single route in the router's
 * configuration.
 *
 * @class Route
 * @namespace Core.Router
 * @module Core
 * @submodule Core.Router
 */
export default class Route {

	/**
	 * Initializes the route.
	 *
	 * @constructor
	 * @method constructor
	 * @param {string} name The unique name of this route, identifying it among
	 *        the rest of the routes in the application.
	 * @param {string} pathExpression A path expression specifying the URL path
	 *        part matching this route (must not contain a query string),
	 *        optionally containing named parameter placeholders specified as
	 *        {@code :parameterName}.
	 * @param {string} controller The full name of Object Container alias
	 *        identifying the controller associated with this route.
	 * @param {string} view The full name or Object Container alias identifying
	 *        the view class associated with this route.
	 * @param {{onlyUpdate: (boolean|function), autoScroll: boolean}} [options]
	 *        The route additional options.
	 */
	constructor(name, pathExpression, controller, view, options) {
		/**
		 * The unique name of this route, identifying it among the rest of the
		 * routes in the application.
		 *
		 * @private
		 * @property _name
		 * @type {string}
		 */
		this._name = name;

		/**
		 * The original URL path expression from which this route was created.
		 *
		 * @private
		 * @property _pathExpression
		 * @type {string}
		 */
		this._pathExpression = pathExpression;

		/**
		 * The full name of Object Container alias identifying the controller
		 * associated with this route.
		 *
		 * @private
		 * @property _controller
		 * @type {string}
		 */
		this._controller = controller;

		/**
		 * The full name or Object Container alias identifying the view class
		 * associated with this route.
		 *
		 * @private
		 * @property _view
		 * @type {Vendor.React.Component}
		 */
		this._view = view;

		/**
		 * The route additional options.
		 *
		 * @private
		 * @property _options
		 * @type {{onlyUpdate: boolean, autoScroll: boolean}}
		 * @default {onlyUpdate: false, autoScroll: true}
		 */
		this._options = Object.assign(
			{ onlyUpdate: false, autoScroll: true },
			options
		);

		/**
		 * The path expression with the trailing slashes trimmed.
		 *
		 * @private
		 * @property _trimmedPathExpression
		 * @type {string}
		 */
		this._trimmedPathExpression = this._getTrimmedPath(pathExpression);

		/**
		 * The names of the parameters in this route.
		 *
		 * @private
		 * @property _parameterNames
		 * @type {string[]}
		 */
		this._parameterNames = this._getParameterNames(pathExpression);

		/**
		 * Set to {@code true} if this route contains parameters in its path.
		 *
		 * @private
		 * @property _hasParameters
		 * @type {boolean}
		 */
		this._hasParameters = !!this._parameterNames.length;

		/**
		 * A regexp used to match URL path against this route and extract the
		 * parameter values from the matched URL paths.
		 *
		 * @private
		 * @property _matcher
		 * @type {RegExp}
		 */
		this._matcher = this._compileToRegExp(this._trimmedPathExpression);
	}

	/**
	 * Creates the URL and query parts of a URL by substituting the route's
	 * parameter placeholders by the provided parameter value.
	 *
	 * The extraneous parameters that do not match any of the route's
	 * placeholders will be appended as the query string.
	 *
	 * @method toPath
	 * @param {Object<string, (number|string)>} [params={}] The route parameter
	 *        values.
	 * @return {string} Path and, if neccessary, query parts of the URL
	 *         representing this route with its parameters replaced by the
	 *         provided parameter values.
	 */
	toPath(params = {}) {
		var path = this._pathExpression;
		var query = [];
		for (var paramName of Object.keys(params)) {
			if (this._isParamInPath(path, paramName)) {
				path = this._substituteParamInPath(path, paramName, params[paramName]);
				path = this._substituteOptionalParamInPath(path, paramName, params[paramName]);
			} else {
				var pair = [paramName, params[paramName]];
				query.push(pair.map(encodeURIComponent).join('='));
			}
		}
		path = this._cleanUnusedOptionalParams(path);

		path = query.length ? path + '?' + query.join('&') : path;
		path = this._getTrimmedPath(path);
		return path;
	}

	/**
	 * Returns the unique identifying name of this route.
	 *
	 * @method getName
	 * @return {string} The name of the route, identifying it.
	 */
	getName() {
		return this._name;
	}

	/**
	 * Returns the full name of the controller to use when this route is
	 * matched by the current URL, or an Object Container-registered alias of
	 * the controller.
	 *
	 * @method getController
	 * @return {string} The name of alias of the controller.
	 */
	getController() {
		return this._controller;
	}

	/**
	 * Returns the full name of the view class or an Object
	 * Container-registered alias for the view class, representing the view to
	 * use when this route is matched by the current URL.
	 *
	 * @method getView
	 * @return {string} The name or alias of the view class.
	 */
	getView() {
		return this._view;
	}

	/**
	 * Return route additional options.
	 *
	 * @method getOptions
	 * @return {{onlyUpdate: (boolean|function), autoScroll: boolean}}
	 */
	getOptions() {
		return this._options;
	}

	/**
	 * Returns the path expression, which is the parametrized pattern matching
	 * the URL paths matched by this route.
	 *
	 * @method getPathExpression
	 * @return {string} The path expression.
	 */
	getPathExpression() {
		return this._pathExpression;
	}

	/**
	 * Tests whether the provided URL path matches this route. The provided
	 * path may contain the query.
	 *
	 * @method matches
	 * @param {string} path The URL path.
	 * @return {boolean} {@code true} if the provided path matches this route.
	 */
	matches(path) {
		var trimmedPath = this._getTrimmedPath(path);
		return this._matcher.test(trimmedPath);
	}

	/**
	 * Extracts the parameter values from the provided path. The method
	 * extracts both the in-path parameters and parses the query, allowing the
	 * query parameters to override the in-path parameters.
	 *
	 * The method returns an empty hash object if the path does not match this
	 * route.
	 *
	 * @method extractParameters
	 * @param {string} path
	 * @return {Object<string, ?string>} Map of parameter names to parameter
	 *         values.
	 */
	extractParameters(path) {
		var trimmedPath = this._getTrimmedPath(path);
		var parameters = this._getParameters(trimmedPath);
		var query = this._getQuery(trimmedPath);

		return Object.assign({}, parameters, query);
	}

	/**
	 *  Replace param placeholder in path with parameter value.
	 *
	 * @private
	 * @method _cleanUnusedOptionalParams
	 * @param {string} path
	 * @param {string} paramName
	 * @param {string} paramValue
	 * @return {string} New path.
	 */
	_substituteParamInPath(path, paramName, paramValue) {
		return path.replace(new RegExp(`(^|\/):${paramName}([\/\?]|$)`), paramValue ? '$1' + paramValue +  '$2' : '');
	}

	/**
	 * Replace optional param placeholder in path with parameter value.
	 *
	 * @private
	 * @method _cleanUnusedOptionalParams
	 * @param {string} path
	 * @param {string} paramName
	 * @param {string} paramValue
	 * @return {string} New path.
	 */
	_substituteOptionalParamInPath(path, paramName, paramValue) {
		return path.replace(new RegExp(`(^|\/):\\\?${paramName}([\/\?]|$)`), paramValue ? '$1' + paramValue +  '$2' : '');
	}

	/**
	 * Replace optional param placeholder in path with parameter value.
	 *
	 * @private
	 * @method _cleanUnusedOptionalParams
	 * @param {string} path
	 * @param {string} paramName
	 * @param {string} paramValue
	 * @return {string} New path.
	 */
	_cleanUnusedOptionalParams(path) {
		return path.replace(/(\/:\?([a-zA-Z0-9_-]+))|(:\?([a-zA-Z0-9_-]+)\/?)/g, '');
	}

	/**
	 * Returns true, if paramName is placed in path.
	 *
	 * @private
	 * @method _isParamInPath
	 * @param {string} path
	 * @param {string} paramName
	 * @return {true}
	 */
	_isParamInPath(path, paramName) {
		var regexp = new RegExp(`:\??${paramName}(?:[\/\?]|$)`);
		return regexp.test(path);
	}

	/**
	 * Compiles the path expression to a regular expression that can be used
	 * for easier matching of URL paths against this route, and extracting the
	 * path parameter values from the URL path.
	 *
	 * @private
	 * @method _compileToRegExp
	 * @param {string} pathExpression The path expression to compile.
	 * @return {RegExp} The compiled regular expression.
	 */
	_compileToRegExp(pathExpression) {
		var pattern = pathExpression
			.replace(LOOSE_SLASHES_REGEXP, '')
			.replace(CONTROL_CHARACTERS_REGEXP, '\\$&');

		// convert parameters to capture sequences
		pattern = pattern.replace(PARAMS_REGEXP_OPT, '(?:$3([^/?]+)$2)?');

		// convert parameters to capture sequences
		pattern = pattern.replace(PARAMS_REGEXP, '([^/?]+)');

		// add path root
		pattern = '^\\\/' + pattern;

		// add query parameters matcher
		var pairPattern = '[^=&;]*(?:=[^&;]*)?';
		pattern += `(?:\\?(?:${pairPattern})(?:[&;]${pairPattern})*)?$`;

		return new RegExp(pattern);
	}

	/**
	 * Parses the provided path and extract the in-path parameters. The method
	 * decodes the parameters and returns them in a hash object.
	 *
	 * @private
	 * @method _getParameters
	 * @param {string} path The URL path.
	 * @return {Object<string, string>} The parsed path parameters.
	 */
	_getParameters(path) {
		if (!this._hasParameters) {
			return {};
		}

		var parameterValues = path.match(this._matcher);
		if (!parameterValues) {
			return {};
		}

		parameterValues.shift(); // remove the match on whole path, and other parts

		return this._extractParameters(parameterValues);
	}

	/**
	 * Extract parameters from given path.
	 *
	 * @private
	 * @method _extractParameters
	 * @param {Array<string>} parameterValues
	 * @return {Object<string, string=>} Params object.
	 */
	_extractParameters(parameterValues) {
		var parameters = {};

		// Cycle for names and values from last to 0
		for (var i = this._parameterNames.length - 1; i >= 0; i--) {
			var cleanParamName = this._cleanOptParamName(this._parameterNames[i]);

			parameters[cleanParamName] = this._decodeURIParameter(parameterValues[i]);
		}

		return parameters;
	}

	/**
	 * Decoding parameters.
	 *
	 * @private
	 * @method _decodeURIParameter
	 * @param {string} parameterValue
	 * @return {string} decodedValue
	 */
	_decodeURIParameter(parameterValue) {
		var decodedValue;
		if (parameterValue) {
			decodedValue = decodeURIComponent(parameterValue);
		}
		return decodedValue;
	}

	/**
	 * Returns optional param name without "?"
	 *
	 * @private
	 * @method _cleanOptParamName
	 * @param {string} paramName Full param name with "?"
	 * @return {string} Strict param name without "?"
	 */
	_cleanOptParamName(paramName) {
		return paramName.replace('?', '');
	}

	/**
	 * Checks if parameter is optional or not.
	 *
	 * @private
	 * @method _isParamOptional
	 * @param {string} paramName
	 * @return {boolean} return true if is optional, otherwise false
	 */
	_isParamOptional(paramName) {
		return /\?.+/.test(paramName);
	}

	/**
	 * Extracts and decodes the query parameters from the provided URL path and
	 * query.
	 *
	 * @private
	 * @method _getQuery
	 * @param {string} path The URL path, including the optional query string
	 *        (if any).
	 * @return {Object<string, ?string>} Parsed query parameters.
	 */
	_getQuery(path) {
		var query = {};
		var queryStart = path.indexOf('?');
		var hasQuery = queryStart > -1 && queryStart !== path.length - 1;

		if (hasQuery) {
			var pairs = path.substring(queryStart + 1).split(/[&;]/);

			for (var parameterPair of pairs) {
				var pair = parameterPair.split('=');
				query[decodeURIComponent(pair[0])] =
					pair.length > 1 ? decodeURIComponent(pair[1]) : true;
			}
		}

		return query;
	}

	/**
	 * Trims the trailing forward slash from the provided URL path.
	 *
	 * @private
	 * @method getLoosesPath
	 * @param {string} path The path to trim.
	 * @return {string} Trimmed path.
	 */
	_getTrimmedPath(path) {
		return `/${path.replace(LOOSE_SLASHES_REGEXP, '')}`;
	}

	/**
	 * Extracts the parameter names from the provided path expression.
	 *
	 * @private
	 * @method _getParameterNames
	 * @param {string} pathExpression The path expression.
	 * @return {string[]} The names of the parameters defined in the provided
	 *         path expression.
	 */
	_getParameterNames(pathExpression) {
		var rawNames = pathExpression.match(PARAMS_REGEXP_UNIVERSAL) || [];

		return rawNames.map(rawParameterName => rawParameterName.substring(1).replace('?', ''));
	}
}

ns.Core.Router.Route = Route;
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var path        = require('path');
var logger      = require('winston');
var _           = require('lodash');

var config      = require('../config.js');

module.exports = {

    /*
     * Set a configuration value.
     *
     * @api public
     */

    set: function(setting, val){
        logger.debug('Setting config value: %s = %s', setting, _.isObject(val) ? JSON.stringify(val, null, 2) : val);
        _.set(config, setting, val);
        return this;
    },

    /*
     * Set a configuration value to true.
     *
     * @api public
     */

    enable: function(setting){
        logger.debug('Enabling %s', setting);
        return _.set(config, setting, true);
    },

    /*
     * Set a configuration value to false
     *
     * @api public
     */

    disable: function(setting){
        logger.debug('Disabling %s', setting);
        return _.set(config, setting, false);
    },

    /*
     * Retrieve a configuration value.
     *
     * @api public
     */

    get: function(setting){
        return _.get(config, setting);
    },

    /*
     * Check if a configuration value is truthy.
     *
     * @api public
     */

    enabled: function(setting){
        return !!this.get(setting);
    },

    /*
     * Check if a configuration value is falsey.
     *
     * @api public
     */

    disabled: function(setting){
        return !this.get(setting);
    },

    /*
     * Run the appropriate service, as specified by the parsed argv parameters
     *
     * @api public
     */

    run: function(argv) {

        this.setPathInfo();
        this.setThemeDetails();
        this.setViewEngine();

        var args = argv._;
        var command = args.shift();
        var opts = argv;
        delete opts._;
        delete opts.$0;

        this.app = require('./application');

        require('./services/run')(command, args, opts);
    },

    /*
     * Set some information about the current path
     *
     * @api private
     */

    setPathInfo: function(){
        this.set('system.paths.root', process.cwd());
    },

    /*
     * Expand the component engine config with the appropriate details.
     *
     * @api private
     */

    setViewEngine: function(){
        var name = this.get('components.view.engine');
        var engine = this.get('engines')[name];
        engine.ext = '.' + _.trim(engine.ext, '.');
        engine.engine = name;
        this.set('components.view.engine', engine);
    },

    /*
     * Fetch theme paths from the theme's json spec and update the theme config info.
     *
     * @api private
     */

    setThemeDetails: function(){
        var theme = this.get('theme');
        var dir = path.parse(require.resolve(theme.name)).dir;
        var themeJSON = require(theme.name);
        ['views','assets','partials','pages'].forEach(function(key){
            theme.paths[key] = path.join(dir, themeJSON[key])
        });
        this.set('theme', theme);
    }

};
<commit_msg>Fix fractal.get() behaviour when called with empty value<commit_after>/**
 * Module dependencies.
 */

var path        = require('path');
var logger      = require('winston');
var _           = require('lodash');

var config      = require('../config.js');

module.exports = {

    /*
     * Set a configuration value.
     *
     * @api public
     */

    set: function(setting, val){
        logger.debug('Setting config value: %s = %s', setting, _.isObject(val) ? JSON.stringify(val, null, 2) : val);
        _.set(config, setting, val);
        return this;
    },

    /*
     * Set a configuration value to true.
     *
     * @api public
     */

    enable: function(setting){
        logger.debug('Enabling %s', setting);
        return _.set(config, setting, true);
    },

    /*
     * Set a configuration value to false
     *
     * @api public
     */

    disable: function(setting){
        logger.debug('Disabling %s', setting);
        return _.set(config, setting, false);
    },

    /*
     * Retrieve a configuration value.
     *
     * @api public
     */

    get: function(setting){
        if (_.isUndefined(setting)) {
            return config;
        }
        return _.get(config, setting);
    },

    /*
     * Check if a configuration value is truthy.
     *
     * @api public
     */

    enabled: function(setting){
        return !!this.get(setting);
    },

    /*
     * Check if a configuration value is falsey.
     *
     * @api public
     */

    disabled: function(setting){
        return !this.get(setting);
    },

    /*
     * Run the appropriate service, as specified by the parsed argv parameters
     *
     * @api public
     */

    run: function(argv) {

        this.setPathInfo();
        this.setThemeDetails();
        this.setViewEngine();

        var args = argv._;
        var command = args.shift();
        var opts = argv;
        delete opts._;
        delete opts.$0;

        this.app = require('./application');

        require('./services/run')(command, args, opts);
    },

    /*
     * Set some information about the current path
     *
     * @api private
     */

    setPathInfo: function(){
        this.set('system.paths.root', process.cwd());
    },

    /*
     * Expand the component engine config with the appropriate details.
     *
     * @api private
     */

    setViewEngine: function(){
        var name = this.get('components.view.engine');
        var engine = this.get('engines')[name];
        engine.ext = '.' + _.trim(engine.ext, '.');
        engine.engine = name;
        this.set('components.view.engine', engine);
    },

    /*
     * Fetch theme paths from the theme's json spec and update the theme config info.
     *
     * @api private
     */

    setThemeDetails: function(){
        var theme = this.get('theme');
        var dir = path.parse(require.resolve(theme.name)).dir;
        var themeJSON = require(theme.name);
        ['views','assets','partials','pages'].forEach(function(key){
            theme.paths[key] = path.join(dir, themeJSON[key])
        });
        this.set('theme', theme);
    }

};
<|endoftext|>


<commit_before>const assertJump = require('./helpers/assertJump');

var BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");

contract('BasicToken', function(accounts) {

  it("should return the correct totalSupply after construction", async function() {
    let token = await BasicTokenMock.new(accounts[0], 100);
    let totalSupply = await token.totalSupply();

    assert.equal(totalSupply, 100);
  })

  it("should return correct balances after transfer", async function(){
    let token = await BasicTokenMock.new(accounts[0], 100);
    let transfer = await token.transfer(accounts[1], 100);

    let firstAccountBalance = await token.balanceOf(accounts[0]);
    assert.equal(firstAccountBalance, 0);

    let secondAccountBalance = await token.balanceOf(accounts[1]);
    assert.equal(secondAccountBalance, 100);
  });

  it('should throw an error when trying to transfer more than balance', async function() {
    let token = await BasicTokenMock.new(accounts[0], 100);
    try {
      let transfer = await token.transfer(accounts[1], 101);
      assert.fail('should have thrown before');
    } catch(error) {
      assertJump(error);
    }
  });

  it('should throw an error when trying to transfer to 0x0', async function() {
    let token = await StandardTokenMock.new(accounts[0], 100);
    try {
      let transfer = await token.transfer(0x0, 100);
      assert.fail('should have thrown before');
    } catch(error) {
      assertJump(error);
    }
  });

});
<commit_msg>fix reference to mock contract<commit_after>const assertJump = require('./helpers/assertJump');

var BasicTokenMock = artifacts.require("./helpers/BasicTokenMock.sol");

contract('BasicToken', function(accounts) {

  it("should return the correct totalSupply after construction", async function() {
    let token = await BasicTokenMock.new(accounts[0], 100);
    let totalSupply = await token.totalSupply();

    assert.equal(totalSupply, 100);
  })

  it("should return correct balances after transfer", async function(){
    let token = await BasicTokenMock.new(accounts[0], 100);
    let transfer = await token.transfer(accounts[1], 100);

    let firstAccountBalance = await token.balanceOf(accounts[0]);
    assert.equal(firstAccountBalance, 0);

    let secondAccountBalance = await token.balanceOf(accounts[1]);
    assert.equal(secondAccountBalance, 100);
  });

  it('should throw an error when trying to transfer more than balance', async function() {
    let token = await BasicTokenMock.new(accounts[0], 100);
    try {
      let transfer = await token.transfer(accounts[1], 101);
      assert.fail('should have thrown before');
    } catch(error) {
      assertJump(error);
    }
  });

  it('should throw an error when trying to transfer to 0x0', async function() {
    let token = await BasicTokenMock.new(accounts[0], 100);
    try {
      let transfer = await token.transfer(0x0, 100);
      assert.fail('should have thrown before');
    } catch(error) {
      assertJump(error);
    }
  });

});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

// Node.js core
const path = require('path');

// Public node modules.
const _ = require('lodash');

// Following this discussion https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric this function is the best implem to determine if a value is a valid number candidate
const isNumeric = (value) => {
  return !isNaN(parseFloat(value)) && isFinite(value);
};

/* eslint-disable prefer-template */
/*
 * Set of utils for models
 */

module.exports = {

  /**
   * Initialize to prevent some mistakes
   */

  initialize: cb => {
    cb();
  },

  /**
   * Find primary key per ORM
   */

  getPK: function (collectionIdentity, collection, models) {
    if (_.isString(collectionIdentity)) {
      const ORM = this.getORM(collectionIdentity);

      try {
        const GraphQLFunctions = require(path.resolve(strapi.config.appPath, 'node_modules', 'strapi-' + ORM, 'lib', 'utils'));

        if (!_.isUndefined(GraphQLFunctions)) {
          return GraphQLFunctions.getPK(collectionIdentity, collection, models || strapi.models);
        }
      } catch (err) {
        return undefined;
      }
    }

    return undefined;
  },

  /**
   * Retrieve the value based on the primary key
   */

  getValuePrimaryKey: (value, defaultKey) => {
    return value[defaultKey] || value.id || value._id;
  },

  /**
   * Find primary key per ORM
   */

  getCount: function (collectionIdentity) {
    if (_.isString(collectionIdentity)) {
      const ORM = this.getORM(collectionIdentity);

      try {
        const ORMFunctions = require(path.resolve(strapi.config.appPath, 'node_modules', 'strapi-' + ORM, 'lib', 'utils'));

        if (!_.isUndefined(ORMFunctions)) {
          return ORMFunctions.getCount(collectionIdentity);
        }
      } catch (err) {
        return undefined;
      }
    }

    return undefined;
  },

  /**
   * Find relation nature with verbose
   */

  getNature: (association, key, models, currentModelName) => {
    try {
      const types = {
        current: '',
        other: ''
      };

      if (_.isUndefined(models)) {
        models = association.plugin ? strapi.plugins[association.plugin].models : strapi.models;
      }

      if ((association.hasOwnProperty('collection') && association.collection === '*') || (association.hasOwnProperty('model') && association.model === '*')) {
        if (association.model) {
          types.current = 'morphToD';
        } else {
          types.current = 'morphTo';
        }

        const flattenedPluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
          Object.keys(strapi.plugins[current].models).forEach((model) => {
            acc[`${current}_${model}`] = strapi.plugins[current].models[model];
          });

          return acc;
        }, {});

        const allModels = _.merge({}, strapi.models, flattenedPluginsModels);

        // We have to find if they are a model linked to this key
        _.forIn(allModels, model => {
          _.forIn(model.attributes, attribute => {
            if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.model === currentModelName) {
              if (attribute.hasOwnProperty('collection')) {
                types.other = 'collection';

                // Break loop
                return false;
              } else if (attribute.hasOwnProperty('model')) {
                types.other = 'model';

                // Break loop
                return false;
              }
            }
          });
        });
      } else if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
        const relatedAttribute = models[association.collection].attributes[association.via];

        if (!relatedAttribute) {
          throw new Error(`The attribute \`${association.via}\` is missing in the model ${_.upperFirst(association.collection)} ${association.plugin ? '(plugin - ' + association.plugin + ')' : '' }`);
        }

        types.current = 'collection';

        if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.collection !== '*' && relatedAttribute.hasOwnProperty('via')) {
          types.other = 'collection';
        } else if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.collection !== '*' && !relatedAttribute.hasOwnProperty('via')) {
          types.other = 'collectionD';
        } else if (relatedAttribute.hasOwnProperty('model') && relatedAttribute.model !== '*') {
          types.other = 'model';
        } else if (relatedAttribute.hasOwnProperty('collection') || relatedAttribute.hasOwnProperty('model')) {
          types.other = 'morphTo';
        }
      } else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) {
        types.current = 'modelD';

        // We have to find if they are a model linked to this key
        const model = models[association.model];
        const attribute = model.attributes[association.via];

        if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.hasOwnProperty('collection') && attribute.collection !== '*') {
          types.other = 'collection';
        } else if (attribute.hasOwnProperty('model') && attribute.model !== '*') {
          types.other = 'model';
        } else if (attribute.hasOwnProperty('collection') || attribute.hasOwnProperty('model')) {
          types.other = 'morphTo';
        }
      } else if (association.hasOwnProperty('model')) {
        types.current = 'model';

        // We have to find if they are a model linked to this key
        _.forIn(models, model => {
          _.forIn(model.attributes, attribute => {
            if (attribute.hasOwnProperty('via') && attribute.via === key) {
              if (attribute.hasOwnProperty('collection')) {
                types.other = 'collection';

                // Break loop
                return false;
              } else if (attribute.hasOwnProperty('model')) {
                types.other = 'modelD';

                // Break loop
                return false;
              }
            }
          });
        });
      } else if (association.hasOwnProperty('collection')) {
        types.current = 'collectionD';

        // We have to find if they are a model linked to this key
        _.forIn(models, model => {
          _.forIn(model.attributes, attribute => {
            if (attribute.hasOwnProperty('via') && attribute.via === key) {
              if (attribute.hasOwnProperty('collection')) {
                types.other = 'collection';

                // Break loop
                return false;
              } else if (attribute.hasOwnProperty('model')) {
                types.other = 'modelD';

                // Break loop
                return false;
              }
            }
          });
        });
      }

      if (types.current === 'collection' && types.other === 'morphTo') {
        return {
          nature: 'manyToManyMorph',
          verbose: 'morphMany'
        };
      } else if (types.current === 'collection' && types.other === 'morphToD') {
        return {
          nature: 'manyToOneMorph',
          verbose: 'morphMany'
        };
      }  else if (types.current === 'modelD' && types.other === 'morphTo') {
        return {
          nature: 'oneToManyMorph',
          verbose: 'morphOne'
        };
      } else if (types.current === 'modelD' && types.other === 'morphToD') {
        return {
          nature: 'oneToOneMorph',
          verbose: 'morphOne'
        };
      } else if (types.current === 'morphToD' && types.other === 'collection') {
        return {
          nature: 'oneMorphToMany',
          verbose: 'belongsToMorph'
        };
      } else if (types.current === 'morphToD' && types.other === 'model') {
        return {
          nature: 'oneMorphToOne',
          verbose: 'belongsToMorph'
        };
      } else if (types.current === 'morphTo' && (types.other === 'model' || association.hasOwnProperty('model'))) {
        return {
          nature: 'manyMorphToOne',
          verbose: 'belongsToManyMorph'
        };
      } else if (types.current === 'morphTo' && (types.other === 'collection' || association.hasOwnProperty('collection'))) {
        return {
          nature: 'manyMorphToMany',
          verbose: 'belongsToManyMorph'
        };
      } else if (types.current === 'modelD' && types.other === 'model') {
        return {
          nature: 'oneToOne',
          verbose: 'belongsTo'
        };
      } else if (types.current === 'model' && types.other === 'modelD') {
        return {
          nature: 'oneToOne',
          verbose: 'hasOne'
        };
      } else if ((types.current === 'model' || types.current === 'modelD') && types.other === 'collection') {
        return {
          nature: 'manyToOne',
          verbose: 'belongsTo'
        };
      } else if (types.current === 'modelD' && types.other === 'collection') {
        return {
          nature: 'oneToMany',
          verbose: 'hasMany'
        };
      } else if (types.current === 'collection' && types.other === 'model') {
        return {
          nature: 'oneToMany',
          verbose: 'hasMany'
        };
      } else if (types.current === 'collection' && types.other === 'collection') {
        return {
          nature: 'manyToMany',
          verbose: 'belongsToMany'
        };
      } else if (types.current === 'collectionD' && types.other === 'collection' || types.current === 'collection' && types.other === 'collectionD') {
        return {
          nature: 'manyToMany',
          verbose: 'belongsToMany'
        };
      } else if (types.current === 'collectionD' && types.other === '') {
        return {
          nature: 'manyWay',
          verbose: 'belongsToMany'
        };
      } else if (types.current === 'model' && types.other === '') {
        return {
          nature: 'oneWay',
          verbose: 'belongsTo'
        };
      }

      return undefined;
    } catch (e) {
      strapi.log.error(`Something went wrong in the model \`${_.upperFirst(currentModelName)}\` with the attribute \`${key}\``);
      strapi.log.error(e);
      strapi.stop();
    }
  },

  /**
   * Return ORM used for this collection.
   */

  getORM: collectionIdentity => {
    return _.get(strapi.models, collectionIdentity.toLowerCase() + '.orm');
  },

  /**
   * Define associations key to models
   */

  defineAssociations: function (model, definition, association, key) {
    try {
      // Initialize associations object
      if (definition.associations === undefined) {
        definition.associations = [];
      }

      // Exclude non-relational attribute
      if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) {
        return undefined;
      }

      // Get relation nature
      let details;
      const globalName = association.model || association.collection || '';
      const infos = this.getNature(association, key, undefined, model.toLowerCase());

      if (globalName !== '*') {
        details = association.plugin ?
          _.get(strapi.plugins, `${association.plugin}.models.${globalName}.attributes.${association.via}`, {}):
          _.get(strapi.models, `${globalName}.attributes.${association.via}`, {});
      }

      // Build associations object
      if (association.hasOwnProperty('collection') && association.collection !== '*') {
        definition.associations.push({
          alias: key,
          type: 'collection',
          collection: association.collection,
          via: association.via || undefined,
          nature: infos.nature,
          autoPopulate: _.get(association, 'autoPopulate', true),
          dominant: details.dominant !== true,
          plugin: association.plugin || undefined,
          filter: details.filter,
        });
      } else if (association.hasOwnProperty('model') && association.model !== '*') {
        definition.associations.push({
          alias: key,
          type: 'model',
          model: association.model,
          via: association.via || undefined,
          nature: infos.nature,
          autoPopulate: _.get(association, 'autoPopulate', true),
          dominant: details.dominant !== true,
          plugin: association.plugin || undefined,
          filter: details.filter,
        });
      } else if (association.hasOwnProperty('collection') || association.hasOwnProperty('model')) {
        const pluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
          Object.keys(strapi.plugins[current].models).forEach((entity) => {
            Object.keys(strapi.plugins[current].models[entity].attributes).forEach((attribute) => {
              const attr = strapi.plugins[current].models[entity].attributes[attribute];

              if (
                (attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
                strapi.plugins[current].models[entity].globalId !== definition.globalId
              ) {
                acc.push(strapi.plugins[current].models[entity].globalId);
              }
            });
          });

          return acc;
        }, []);

        const appModels = Object.keys(strapi.models).reduce((acc, entity) => {
          Object.keys(strapi.models[entity].attributes).forEach((attribute) => {
            const attr = strapi.models[entity].attributes[attribute];

            if (
              (attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
              strapi.models[entity].globalId !== definition.globalId
            ) {
              acc.push(strapi.models[entity].globalId);
            }
          });

          return acc;
        }, []);

        const models = _.uniq(appModels.concat(pluginsModels));

        definition.associations.push({
          alias: key,
          type: association.model ? 'model' : 'collection',
          related: models,
          nature: infos.nature,
          autoPopulate: _.get(association, 'autoPopulate', true),
          filter: association.filter,
        });
      }
    } catch (e) {
      strapi.log.error(`Something went wrong in the model \`${_.upperFirst(model)}\` with the attribute \`${key}\``);
      strapi.log.error(e);
      strapi.stop();
    }
  },

  getVia: (attribute, association) => {
    return _.findKey(strapi.models[association.model || association.collection].attributes, {via: attribute});
  },

  convertParams: (entity, params) => {
    if (!entity) {
      throw new Error('You can\'t call the convert params method without passing the model\'s name as a first argument.');
    }

    // Remove the source params (that can be sent from the ctm plugin) since it is not a filter
    if (params.source) {
      delete params.source;
    }

    const model = entity.toLowerCase();

    const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
      _.assign(acc, _.get(strapi.plugins[current], ['models'], {}));
      return acc;
    }, {}));

    if (!models.hasOwnProperty(model)) {
      return this.log.error(`The model ${model} can't be found.`);
    }

    const client = models[model].client;
    const connector = models[model].orm;

    if (!connector) {
      throw new Error(`Impossible to determine the ORM used for the model ${model}.`);
    }

    const convertor = strapi.hook[connector].load().getQueryParams;
    const convertParams = {
      where: {},
      sort: '',
      start: 0,
      limit: 100
    };

    _.forEach(params, (value, key)  => {
      let result;
      let formattedValue;

      // Check if the value if a valid candidate to be converted to a number value
      formattedValue = isNumeric(value)
        ? _.toNumber(value)
        : value;

      if (_.includes(['_start', '_limit'], key)) {
        result = convertor(formattedValue, key);
      } else if (key === '_sort') {
        const [attr, order = 'ASC'] = formattedValue.split(':');
        result = convertor(order, key, attr);
      } else {
        const suffix = key.split('_');

        // Mysql stores boolean as 1 or 0
        if (client === 'mysql' && _.get(models, [model, 'attributes', suffix, 'type']) === 'boolean') {
          formattedValue = value === 'true' ? '1' : '0';
        }

        let type;

        if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss', 'in'], _.last(suffix))) {
          type = `_${_.last(suffix)}`;
          key = _.dropRight(suffix).join('_');
        } else {
          type = '=';
        }

        result = convertor(formattedValue, type, key);
      }

      _.set(convertParams, result.key, result.value);
    });

    return convertParams;
  }
};
<commit_msg>Skip number conversion on string fields<commit_after>'use strict';

/**
 * Module dependencies
 */

// Node.js core
const path = require('path');

// Public node modules.
const _ = require('lodash');

// Following this discussion https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric this function is the best implem to determine if a value is a valid number candidate
const isNumeric = (value) => {
  return !isNaN(parseFloat(value)) && isFinite(value);
};

/* eslint-disable prefer-template */
/*
 * Set of utils for models
 */

module.exports = {

  /**
   * Initialize to prevent some mistakes
   */

  initialize: cb => {
    cb();
  },

  /**
   * Find primary key per ORM
   */

  getPK: function (collectionIdentity, collection, models) {
    if (_.isString(collectionIdentity)) {
      const ORM = this.getORM(collectionIdentity);

      try {
        const GraphQLFunctions = require(path.resolve(strapi.config.appPath, 'node_modules', 'strapi-' + ORM, 'lib', 'utils'));

        if (!_.isUndefined(GraphQLFunctions)) {
          return GraphQLFunctions.getPK(collectionIdentity, collection, models || strapi.models);
        }
      } catch (err) {
        return undefined;
      }
    }

    return undefined;
  },

  /**
   * Retrieve the value based on the primary key
   */

  getValuePrimaryKey: (value, defaultKey) => {
    return value[defaultKey] || value.id || value._id;
  },

  /**
   * Find primary key per ORM
   */

  getCount: function (collectionIdentity) {
    if (_.isString(collectionIdentity)) {
      const ORM = this.getORM(collectionIdentity);

      try {
        const ORMFunctions = require(path.resolve(strapi.config.appPath, 'node_modules', 'strapi-' + ORM, 'lib', 'utils'));

        if (!_.isUndefined(ORMFunctions)) {
          return ORMFunctions.getCount(collectionIdentity);
        }
      } catch (err) {
        return undefined;
      }
    }

    return undefined;
  },

  /**
   * Find relation nature with verbose
   */

  getNature: (association, key, models, currentModelName) => {
    try {
      const types = {
        current: '',
        other: ''
      };

      if (_.isUndefined(models)) {
        models = association.plugin ? strapi.plugins[association.plugin].models : strapi.models;
      }

      if ((association.hasOwnProperty('collection') && association.collection === '*') || (association.hasOwnProperty('model') && association.model === '*')) {
        if (association.model) {
          types.current = 'morphToD';
        } else {
          types.current = 'morphTo';
        }

        const flattenedPluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
          Object.keys(strapi.plugins[current].models).forEach((model) => {
            acc[`${current}_${model}`] = strapi.plugins[current].models[model];
          });

          return acc;
        }, {});

        const allModels = _.merge({}, strapi.models, flattenedPluginsModels);

        // We have to find if they are a model linked to this key
        _.forIn(allModels, model => {
          _.forIn(model.attributes, attribute => {
            if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.model === currentModelName) {
              if (attribute.hasOwnProperty('collection')) {
                types.other = 'collection';

                // Break loop
                return false;
              } else if (attribute.hasOwnProperty('model')) {
                types.other = 'model';

                // Break loop
                return false;
              }
            }
          });
        });
      } else if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
        const relatedAttribute = models[association.collection].attributes[association.via];

        if (!relatedAttribute) {
          throw new Error(`The attribute \`${association.via}\` is missing in the model ${_.upperFirst(association.collection)} ${association.plugin ? '(plugin - ' + association.plugin + ')' : '' }`);
        }

        types.current = 'collection';

        if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.collection !== '*' && relatedAttribute.hasOwnProperty('via')) {
          types.other = 'collection';
        } else if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.collection !== '*' && !relatedAttribute.hasOwnProperty('via')) {
          types.other = 'collectionD';
        } else if (relatedAttribute.hasOwnProperty('model') && relatedAttribute.model !== '*') {
          types.other = 'model';
        } else if (relatedAttribute.hasOwnProperty('collection') || relatedAttribute.hasOwnProperty('model')) {
          types.other = 'morphTo';
        }
      } else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) {
        types.current = 'modelD';

        // We have to find if they are a model linked to this key
        const model = models[association.model];
        const attribute = model.attributes[association.via];

        if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.hasOwnProperty('collection') && attribute.collection !== '*') {
          types.other = 'collection';
        } else if (attribute.hasOwnProperty('model') && attribute.model !== '*') {
          types.other = 'model';
        } else if (attribute.hasOwnProperty('collection') || attribute.hasOwnProperty('model')) {
          types.other = 'morphTo';
        }
      } else if (association.hasOwnProperty('model')) {
        types.current = 'model';

        // We have to find if they are a model linked to this key
        _.forIn(models, model => {
          _.forIn(model.attributes, attribute => {
            if (attribute.hasOwnProperty('via') && attribute.via === key) {
              if (attribute.hasOwnProperty('collection')) {
                types.other = 'collection';

                // Break loop
                return false;
              } else if (attribute.hasOwnProperty('model')) {
                types.other = 'modelD';

                // Break loop
                return false;
              }
            }
          });
        });
      } else if (association.hasOwnProperty('collection')) {
        types.current = 'collectionD';

        // We have to find if they are a model linked to this key
        _.forIn(models, model => {
          _.forIn(model.attributes, attribute => {
            if (attribute.hasOwnProperty('via') && attribute.via === key) {
              if (attribute.hasOwnProperty('collection')) {
                types.other = 'collection';

                // Break loop
                return false;
              } else if (attribute.hasOwnProperty('model')) {
                types.other = 'modelD';

                // Break loop
                return false;
              }
            }
          });
        });
      }

      if (types.current === 'collection' && types.other === 'morphTo') {
        return {
          nature: 'manyToManyMorph',
          verbose: 'morphMany'
        };
      } else if (types.current === 'collection' && types.other === 'morphToD') {
        return {
          nature: 'manyToOneMorph',
          verbose: 'morphMany'
        };
      }  else if (types.current === 'modelD' && types.other === 'morphTo') {
        return {
          nature: 'oneToManyMorph',
          verbose: 'morphOne'
        };
      } else if (types.current === 'modelD' && types.other === 'morphToD') {
        return {
          nature: 'oneToOneMorph',
          verbose: 'morphOne'
        };
      } else if (types.current === 'morphToD' && types.other === 'collection') {
        return {
          nature: 'oneMorphToMany',
          verbose: 'belongsToMorph'
        };
      } else if (types.current === 'morphToD' && types.other === 'model') {
        return {
          nature: 'oneMorphToOne',
          verbose: 'belongsToMorph'
        };
      } else if (types.current === 'morphTo' && (types.other === 'model' || association.hasOwnProperty('model'))) {
        return {
          nature: 'manyMorphToOne',
          verbose: 'belongsToManyMorph'
        };
      } else if (types.current === 'morphTo' && (types.other === 'collection' || association.hasOwnProperty('collection'))) {
        return {
          nature: 'manyMorphToMany',
          verbose: 'belongsToManyMorph'
        };
      } else if (types.current === 'modelD' && types.other === 'model') {
        return {
          nature: 'oneToOne',
          verbose: 'belongsTo'
        };
      } else if (types.current === 'model' && types.other === 'modelD') {
        return {
          nature: 'oneToOne',
          verbose: 'hasOne'
        };
      } else if ((types.current === 'model' || types.current === 'modelD') && types.other === 'collection') {
        return {
          nature: 'manyToOne',
          verbose: 'belongsTo'
        };
      } else if (types.current === 'modelD' && types.other === 'collection') {
        return {
          nature: 'oneToMany',
          verbose: 'hasMany'
        };
      } else if (types.current === 'collection' && types.other === 'model') {
        return {
          nature: 'oneToMany',
          verbose: 'hasMany'
        };
      } else if (types.current === 'collection' && types.other === 'collection') {
        return {
          nature: 'manyToMany',
          verbose: 'belongsToMany'
        };
      } else if (types.current === 'collectionD' && types.other === 'collection' || types.current === 'collection' && types.other === 'collectionD') {
        return {
          nature: 'manyToMany',
          verbose: 'belongsToMany'
        };
      } else if (types.current === 'collectionD' && types.other === '') {
        return {
          nature: 'manyWay',
          verbose: 'belongsToMany'
        };
      } else if (types.current === 'model' && types.other === '') {
        return {
          nature: 'oneWay',
          verbose: 'belongsTo'
        };
      }

      return undefined;
    } catch (e) {
      strapi.log.error(`Something went wrong in the model \`${_.upperFirst(currentModelName)}\` with the attribute \`${key}\``);
      strapi.log.error(e);
      strapi.stop();
    }
  },

  /**
   * Return ORM used for this collection.
   */

  getORM: collectionIdentity => {
    return _.get(strapi.models, collectionIdentity.toLowerCase() + '.orm');
  },

  /**
   * Define associations key to models
   */

  defineAssociations: function (model, definition, association, key) {
    try {
      // Initialize associations object
      if (definition.associations === undefined) {
        definition.associations = [];
      }

      // Exclude non-relational attribute
      if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) {
        return undefined;
      }

      // Get relation nature
      let details;
      const globalName = association.model || association.collection || '';
      const infos = this.getNature(association, key, undefined, model.toLowerCase());

      if (globalName !== '*') {
        details = association.plugin ?
          _.get(strapi.plugins, `${association.plugin}.models.${globalName}.attributes.${association.via}`, {}):
          _.get(strapi.models, `${globalName}.attributes.${association.via}`, {});
      }

      // Build associations object
      if (association.hasOwnProperty('collection') && association.collection !== '*') {
        definition.associations.push({
          alias: key,
          type: 'collection',
          collection: association.collection,
          via: association.via || undefined,
          nature: infos.nature,
          autoPopulate: _.get(association, 'autoPopulate', true),
          dominant: details.dominant !== true,
          plugin: association.plugin || undefined,
          filter: details.filter,
        });
      } else if (association.hasOwnProperty('model') && association.model !== '*') {
        definition.associations.push({
          alias: key,
          type: 'model',
          model: association.model,
          via: association.via || undefined,
          nature: infos.nature,
          autoPopulate: _.get(association, 'autoPopulate', true),
          dominant: details.dominant !== true,
          plugin: association.plugin || undefined,
          filter: details.filter,
        });
      } else if (association.hasOwnProperty('collection') || association.hasOwnProperty('model')) {
        const pluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
          Object.keys(strapi.plugins[current].models).forEach((entity) => {
            Object.keys(strapi.plugins[current].models[entity].attributes).forEach((attribute) => {
              const attr = strapi.plugins[current].models[entity].attributes[attribute];

              if (
                (attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
                strapi.plugins[current].models[entity].globalId !== definition.globalId
              ) {
                acc.push(strapi.plugins[current].models[entity].globalId);
              }
            });
          });

          return acc;
        }, []);

        const appModels = Object.keys(strapi.models).reduce((acc, entity) => {
          Object.keys(strapi.models[entity].attributes).forEach((attribute) => {
            const attr = strapi.models[entity].attributes[attribute];

            if (
              (attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
              strapi.models[entity].globalId !== definition.globalId
            ) {
              acc.push(strapi.models[entity].globalId);
            }
          });

          return acc;
        }, []);

        const models = _.uniq(appModels.concat(pluginsModels));

        definition.associations.push({
          alias: key,
          type: association.model ? 'model' : 'collection',
          related: models,
          nature: infos.nature,
          autoPopulate: _.get(association, 'autoPopulate', true),
          filter: association.filter,
        });
      }
    } catch (e) {
      strapi.log.error(`Something went wrong in the model \`${_.upperFirst(model)}\` with the attribute \`${key}\``);
      strapi.log.error(e);
      strapi.stop();
    }
  },

  getVia: (attribute, association) => {
    return _.findKey(strapi.models[association.model || association.collection].attributes, {via: attribute});
  },

  convertParams: (entity, params) => {
    if (!entity) {
      throw new Error('You can\'t call the convert params method without passing the model\'s name as a first argument.');
    }

    // Remove the source params (that can be sent from the ctm plugin) since it is not a filter
    if (params.source) {
      delete params.source;
    }

    const model = entity.toLowerCase();

    const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
      _.assign(acc, _.get(strapi.plugins[current], ['models'], {}));
      return acc;
    }, {}));

    if (!models.hasOwnProperty(model)) {
      return this.log.error(`The model ${model} can't be found.`);
    }

    const client = models[model].client;
    const connector = models[model].orm;

    if (!connector) {
      throw new Error(`Impossible to determine the ORM used for the model ${model}.`);
    }

    const convertor = strapi.hook[connector].load().getQueryParams;
    const convertParams = {
      where: {},
      sort: '',
      start: 0,
      limit: 100
    };

    _.forEach(params, (value, key)  => {
      let result;
      let formattedValue;
      let modelAttributes = models[model]['attributes'];
      let fieldType;
      // Get the field type to later check if it's a string before number conversion
      if (modelAttributes[key]) {
        fieldType = modelAttributes[key]['type'];
      } else {
        let splitKey = key.split('_').pop();
        if (modelAttributes[splitKey]) {
          fieldType = modelAttributes[splitKey]['type'];
        }
      }
      // Check if the value is a valid candidate to be converted to a number value
      if (fieldType != 'string') {
        formattedValue = isNumeric(value)
          ? _.toNumber(value)
          : value;
      } else {
        formattedValue = value;
      }

      if (_.includes(['_start', '_limit'], key)) {
        result = convertor(formattedValue, key);
      } else if (key === '_sort') {
        const [attr, order = 'ASC'] = formattedValue.split(':');
        result = convertor(order, key, attr);
      } else {
        const suffix = key.split('_');

        // Mysql stores boolean as 1 or 0
        if (client === 'mysql' && _.get(models, [model, 'attributes', suffix, 'type']) === 'boolean') {
          formattedValue = value === 'true' ? '1' : '0';
        }

        let type;

        if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss', 'in'], _.last(suffix))) {
          type = `_${_.last(suffix)}`;
          key = _.dropRight(suffix).join('_');
        } else {
          type = '=';
        }

        result = convertor(formattedValue, type, key);
      }

      _.set(convertParams, result.key, result.value);
    });

    return convertParams;
  }
};
<|endoftext|>


<commit_before>/*
var http = require('http');
var url = require("url");

function start() {
	function onRequest(request, response) {
		var pathname = url.parse(request.url).pathname;
		console.log("Request received for " + pathname);
	    response.writeHead(200, {'Content-Type': 'text/plain'});
	    response.write('Hello World\n');
	    response.end();
	}

	http.createServer(onRequest).listen(8888);
	console.log("Server has started.");
}

exports.start = start;
*/<commit_msg>test hello world<commit_after>/*
var http = require('http');
var url = require("url");

function start() {
	function onRequest(request, response) {
		var pathname = url.parse(request.url).pathname;
		console.log("Request received for " + pathname);
	    response.writeHead(200, {'Content-Type': 'text/plain'});
	    response.write('Hello World\n');
	    response.end();
	}

	http.createServer(onRequest).listen(8888);
	console.log("Server has started.");
}

exports.start = start;
*/
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');<|endoftext|>