<commit_before>"use strict";

const fs = require("fs");
const Graph = require("../graph");
const keepLogsFor = 45;

/**
 * Initiate database and logging and eventually skype counterpart
 */
if (!Monitor.statistics) {
    Monitor.statistics = {};

    Monitor.statistics.database = require("../database")("config/room-stats");

    /**
     * Set up logging for all rooms, but only do the logging in rooms that have a db key.
     */
    Events.on(["c", "c:"], (id, room, msgType, msg) => {
        if (!Monitor.statistics.database.hasKey(room.id)) return;
        msg = msg.replace(/(?:\|)?[0-9]+\|/i, ""); // remove timestamp

        let [user, message] = msg.split("|");
        let userid = toId(user);

        if (!userid) return; // this is a serverside message

        // log non moderation counts.
        if (msg.indexOf("/log") !== 0) {
            logStat(room.id, userid);
        }
    });

    require("../skype/index"); // init skype component
}

let db = Monitor.statistics.database;

function logStat (roomid, userid) {
    let hours = new Date().getHours();
    let date = new Date().toLocaleDateString();
    let obj = db(roomid).get([userid, date], {});
    if (!obj[hours]) obj[hours] = 0;

    obj[hours]++;
    db(roomid).set([userid, date], obj);
}

/**
 * Run searches
 *
 * This all round function allows 3 different searches, timezone, userstats and room stats
 * each calling a function from inside a promise.
 *
 * Sorting may take some time so this prevents I/O blocking
 */
const runSearch = Monitor.statistics.runSearch = function (aspect, roomid, detail) {
    return new Promise((resolve, reject) => {
        if (!db.hasKey(roomid)) return reject("Logging not enabled for room.");
        let result;
        switch(aspect) {
            case "timezone":
                result = runTimeZoneSearch(roomid, detail);
                resolve(result);
                break;
            case "roomstats":
                result = runRoomSearch(roomid, detail);
                resolve(result);
                break;
            case "userstats":
                result = runUserSearch(roomid, detail);
                resolve(result);
                break;
            default:
                reject("Invalid search key.");
                break;
        }
    });
};

function runTimeZoneSearch (roomid, userid) {
    let data = db(roomid).get(userid);
    let stats = {};

    let days = 0;

    for (let i = 0; i < 24; i++) {
        stats[i] = 0;
    }
    for (let date in data) {
        days++;
        let dayStats = data[date];
        for (let hour in dayStats) {
            stats[hour] += dayStats[hour];
        }
    }

    if (days > 1) {
        for (let hour in stats) {
            stats[hour] = Math.ceil(stats[hour] / days);
        }
    }

    return stats;
}

function runRoomSearch(roomid, date) {
    let data = Object.assign({}, db(roomid).cache);

    let stats = {};
    for (let userid in data) {
        if (!data[userid][date]) continue;
        stats[userid] = 0;

        for (let hour in data[userid][date]) {
            stats[userid] += data[userid][date][hour];
        }
    }

    return stats;
}

function runUserSearch (roomid, details) {
    let data = db(roomid).get(details, {});
    let stats = {};
    for (let d in data) {
        stats[d] = 0;
        for (let h in data[d]) {
            stats[d] += data[d][h];
        }
    }

    return stats;
}

function cleanStatistics () {
    // do this async to prevent any I/O blocking
    let cutoff = Date.now() - (keepLogsFor * 24 * 60 * 60000);
    Promise.resolve(
        db.keys().forEach(key => {
            let data = db(key).cache;
            for (let u in data) {
                let ud = data[u];
                for (let d in ud) {
                    if (Date.parse(new Date(d)) < cutoff) db(key).delete([u, d]);
                }
            }
        })
    ).then(() => db.write());
}

setImmediate(() => cleanStatistics());

exports.commands = {
    enablelogging: function (target, room, user) {
        if (!this.can("dev")) return false;

        let roomid = toId(target, true);
        if (!roomid || !Rooms.rooms.has(roomid)) return this.send("Invalid room.");

        if (db.hasKey(roomid)) return this.send("Aggregate user statistic logging for '" + roomid + "' is already enabled.");

        db.spawn(roomid);
        this.send("You have enabled aggregate user statistic logging for '" + roomid + "'.");
    },

    disablelogging: function (target, room, user) {
        if (!this.can("dev")) return false;

        let roomid = toId(target, true);
        if (!roomid) return this.send("Invalid room.");

        if (db.hasKey(roomid)) return this.send("Aggregate user statistic logging for '" + roomid + "' is not enabled.");

        db.spawn(roomid);
        this.send("You have disabled aggregate user statistic logging for '" + room.id + "'.");
    },

    requestkey: function (target, room, user) {
        let targetRoom = Rooms.rooms.has(toId(target)) ? Rooms.get(target) : null;
        room = targetRoom || room;

        if (!room) return user.sendTo("Invalid room.");

        if (!user.hasRank(room, "#")) return this.send("Access denied.");

        let key = (Math.floor(Math.random() * 0xFFFFFFFFFFFFF) + 0x1000000000000).toString(16).toUpperCase();
        db("auth-keys").set([room.id, "key"], key);

        user.sendTo("Your authentication key pair is: " + key + ".  Use ``+authenticate " + room.id + ", " + key + "`` on skype to complete the pairing sequence.");
    },

    userstats: function (target, room, user) {
        if (!room) {
            [room, target] = target.split(",").map(p => p.trim());
            if (!Rooms.rooms.has(toId(room))) return this.send("Invalid room.");

            room = Rooms.get(room);
        }

        if (!user.hasRank(room, "%")) return this.send("Access denied.");

        if (!target) return this.parse("/help userstats");

        runSearch("userstats", room.id, toId(target)).then(data => {
            Graph(data, {
                maxBars: 40,
                title: "User statistics for '" + toId(target) + "' in " + room.name,
            }).then(graph => {
                Tools.uploadToHastebin(graph, link => {
                    user.sendTo(link);
                });
            }).catch(err => {
                user.sendTo("GRAPH ERROR: " + err);
                console.log(err.stack);
            });
        }).catch(err => {
            user.sendTo("SEARCH ERROR: " + err);
            console.log(err.stack);
        });
    },

    usertimezone: function (target, room, user) {
        if (!room) {
            [room, target] = target.split(",").map(p => p.trim());
            if (!Rooms.rooms.has(toId(room))) return this.send("Invalid room.");

            room = Rooms.get(room);
        }

        if (!user.hasRank(room, "%")) return this.send("Access denied.");

        if (!target) return this.parse("/help usertimezone");

        runSearch("timezone", room.id, toId(target)).then(data => {
            Graph(data, {
                maxBars: 40,
                title: "Timezone statistics for '" + toId(target) + "' in " + room.name,
            }).then(graph => {
                Tools.uploadToHastebin(graph, link => {
                    user.sendTo(link);
                });
            }).catch(err => {
                user.sendTo("GRAPH ERROR: " + err);
                console.log(err.stack);
            });
        }).catch(err => {
            user.sendTo("SEARCH ERROR: " + err);
            console.log(err.stack);
        });
    },

    roomstats: function (target, room, user) {
        if (!room) {
            [room, target] = target.split(",").map(p => p.trim());
            if (!Rooms.rooms.has(toId(room))) return this.send("Invalid room.");

            room = Rooms.get(room);
        }

        if (!user.hasRank(room, "%")) return this.send("Access denied.");

        if (!target) return this.parse("/help roomstats");

        runSearch("roomstats", room.id, target).then(data => {
            Graph(data, {
                maxBars: 40,
                title: "Room statistics for '" + room.id + "' on " + target,
                sort: "values",
            }).then(graph => {
                Tools.uploadToHastebin(graph, link => {
                    user.sendTo(link);
                });
            }).catch(err => {
                user.sendTo("GRAPH ERROR: " + err);
                console.log(err.stack);
            });
        }).catch(err => {
            user.sendTo("SEARCH ERROR: " + err);
            console.log(err.stack);
        });
    },
    statshelp: function (target, room, user) {
        this.send("Stats guide: http://pastebin.com/iZCBbynu");
    },
};
<commit_msg>Update room-stats.js<commit_after>"use strict";

const fs = require("fs");
const Graph = require("../graph");
const keepLogsFor = 45;

/**
 * Initiate database and logging and eventually skype counterpart
 */
if (!Monitor.statistics) {
    Monitor.statistics = {};

    Monitor.statistics.database = require("../database")("config/room-stats");

    /**
     * Set up logging for all rooms, but only do the logging in rooms that have a db key.
     */
    Events.on(["c", "c:"], (id, room, msgType, msg) => {
        if (!Monitor.statistics.database.hasKey(room.id)) return;
        msg = msg.replace(/(?:\|)?[0-9]+\|/i, ""); // remove timestamp

        let [user, message] = msg.split("|");
        let userid = toId(user);

        if (!userid) return; // this is a serverside message

        // log non moderation counts.
        if (msg.indexOf("/log") !== 0) {
            logStat(room.id, userid);
        }
    });

    require("../skype/index"); // init skype component
}

let db = Monitor.statistics.database;

function logStat (roomid, userid) {
    let hours = new Date().getHours();
    let date = new Date().toLocaleDateString();
    let obj = db(roomid).get([userid, date], {});
    if (!obj[hours]) obj[hours] = 0;

    obj[hours]++;
    db(roomid).set([userid, date], obj);
}

/**
 * Run searches
 *
 * This all round function allows 3 different searches, timezone, userstats and room stats
 * each calling a function from inside a promise.
 *
 * Sorting may take some time so this prevents I/O blocking
 */
const runSearch = Monitor.statistics.runSearch = function (aspect, roomid, detail) {
    return new Promise((resolve, reject) => {
        if (!db.hasKey(roomid)) return reject("Logging not enabled for room.");
        let result;
        switch(aspect) {
            case "timezone":
                result = runTimeZoneSearch(roomid, detail);
                resolve(result);
                break;
            case "roomstats":
                result = runRoomSearch(roomid, detail);
                resolve(result);
                break;
            case "userstats":
                result = runUserSearch(roomid, detail);
                resolve(result);
                break;
            default:
                reject("Invalid search key.");
                break;
        }
    });
};

function runTimeZoneSearch (roomid, userid) {
    let data = db(roomid).get(userid);
    let stats = {};

    let days = 0;

    for (let i = 0; i < 24; i++) {
        stats[i] = 0;
    }
    for (let date in data) {
        days++;
        let dayStats = data[date];
        for (let hour in dayStats) {
            stats[hour] += dayStats[hour];
        }
    }

    if (days > 1) {
        for (let hour in stats) {
            stats[hour] = Math.ceil(stats[hour] / days);
        }
    }

    return stats;
}

function runRoomSearch(roomid, date) {
    let data = Object.assign({}, db(roomid).cache);

    let stats = {};
    for (let userid in data) {
        if (date && !data[userid][date]) continue;
        stats[userid] = 0;
        if (!date) {
            // total search
            for (let d in data[userid]) {
                for (let h in data[userid][d]) {
                    stats[userid] += data[userid][d][h];
                }
            }
            continue;
        } else {
            // search for that one day
            for (let hour in data[userid][date]) {
                stats[userid] += data[userid][date][hour];
            }
        }
    }

    return stats;
}

function runUserSearch (roomid, details) {
    let data = db(roomid).get(details, {});
    let stats = {};
    for (let d in data) {
        stats[d] = 0;
        for (let h in data[d]) {
            stats[d] += data[d][h];
        }
    }

    return stats;
}

function cleanStatistics () {
    // do this async to prevent any I/O blocking
    let cutoff = Date.now() - (keepLogsFor * 24 * 60 * 60000);
    Promise.resolve(
        db.keys().forEach(key => {
            let data = db(key).cache;
            for (let u in data) {
                let ud = data[u];
                for (let d in ud) {
                    if (Date.parse(new Date(d)) < cutoff) db(key).delete([u, d]);
                }
            }
        })
    ).then(() => db.write());
}

setImmediate(() => cleanStatistics());

exports.commands = {
    enablelogging: function (target, room, user) {
        if (!this.can("dev")) return false;

        let roomid = toId(target, true);
        if (!roomid || !Rooms.rooms.has(roomid)) return this.send("Invalid room.");

        if (db.hasKey(roomid)) return this.send("Aggregate user statistic logging for '" + roomid + "' is already enabled.");

        db.spawn(roomid);
        this.send("You have enabled aggregate user statistic logging for '" + roomid + "'.");
    },

    disablelogging: function (target, room, user) {
        if (!this.can("dev")) return false;

        let roomid = toId(target, true);
        if (!roomid) return this.send("Invalid room.");

        if (db.hasKey(roomid)) return this.send("Aggregate user statistic logging for '" + roomid + "' is not enabled.");

        db.spawn(roomid);
        this.send("You have disabled aggregate user statistic logging for '" + room.id + "'.");
    },

    requestkey: function (target, room, user) {
        let targetRoom = Rooms.rooms.has(toId(target)) ? Rooms.get(target) : null;
        room = targetRoom || room;

        if (!room) return user.sendTo("Invalid room.");

        if (!user.hasRank(room, "#")) return this.send("Access denied.");

        let key = (Math.floor(Math.random() * 0xFFFFFFFFFFFFF) + 0x1000000000000).toString(16).toUpperCase();
        db("auth-keys").set([room.id, "key"], key);

        user.sendTo("Your authentication key pair is: " + key + ".  Use ``+authenticate " + room.id + ", " + key + "`` on skype to complete the pairing sequence.");
    },

    userstats: function (target, room, user) {
        if (!room) {
            [room, target] = target.split(",").map(p => p.trim());
            if (!Rooms.rooms.has(toId(room))) return this.send("Invalid room.");

            room = Rooms.get(room);
        }

        if (!user.hasRank(room, "%")) return this.send("Access denied.");

        if (!target) return this.parse("/help userstats");

        runSearch("userstats", room.id, toId(target)).then(data => {
            Graph(data, {
                maxBars: 40,
                title: "User statistics for '" + toId(target) + "' in " + room.name,
            }).then(graph => {
                Tools.uploadToHastebin(graph, link => {
                    user.sendTo(link);
                });
            }).catch(err => {
                user.sendTo("GRAPH ERROR: " + err);
                console.log(err.stack);
            });
        }).catch(err => {
            user.sendTo("SEARCH ERROR: " + err);
            console.log(err.stack);
        });
    },

    usertimezone: function (target, room, user) {
        if (!room) {
            [room, target] = target.split(",").map(p => p.trim());
            if (!Rooms.rooms.has(toId(room))) return this.send("Invalid room.");

            room = Rooms.get(room);
        }

        if (!user.hasRank(room, "%")) return this.send("Access denied.");

        if (!target) return this.parse("/help usertimezone");

        runSearch("timezone", room.id, toId(target)).then(data => {
            Graph(data, {
                maxBars: 40,
                title: "Timezone statistics for '" + toId(target) + "' in " + room.name,
            }).then(graph => {
                Tools.uploadToHastebin(graph, link => {
                    user.sendTo(link);
                });
            }).catch(err => {
                user.sendTo("GRAPH ERROR: " + err);
                console.log(err.stack);
            });
        }).catch(err => {
            user.sendTo("SEARCH ERROR: " + err);
            console.log(err.stack);
        });
    },

    roomstats: function (target, room, user) {
        if (!room) {
            [room, target] = target.split(",").map(p => p.trim());
            if (!Rooms.rooms.has(toId(room))) return this.send("Invalid room.");

            room = Rooms.get(room);
        }

        if (!user.hasRank(room, "%")) return this.send("Access denied.");

        runSearch("roomstats", room.id, target || null).then(data => {
            Graph(data, {
                maxBars: 40,
                title: "Room statistics for '" + room.id + (target ? "' on " + target : ""),
                sort: "values",
            }).then(graph => {
                Tools.uploadToHastebin(graph, link => {
                    user.sendTo(link);
                });
            }).catch(err => {
                user.sendTo("GRAPH ERROR: " + err);
                console.log(err.stack);
            });
        }).catch(err => {
            user.sendTo("SEARCH ERROR: " + err);
            console.log(err.stack);
        });
    },
    statshelp: function (target, room, user) {
        this.send("Stats guide: http://pastebin.com/iZCBbynu");
    },
};
<|endoftext|>


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

"use strict";

var OpenSpeedMonitor = OpenSpeedMonitor || {};

OpenSpeedMonitor.DateTimePicker = function(dateTimePickerElement, autoTime) {
	dateTimePickerElement = $(dateTimePickerElement);
	autoTime = autoTime || "00:00";

	var dateInput = dateTimePickerElement.find(".date-control input[type='text']");
	var timeInput = dateTimePickerElement.find(".time-control input[type='text']");
	var timeManualCheckbox = dateTimePickerElement.find(".time-control input[type='checkbox']");
	var dateHiddenValue = dateTimePickerElement.find("input.date-hidden");
	var timeHiddenValue = dateTimePickerElement.find("input.time-hidden");
    var defaultDatePickerOptions = { endDate: "+0d" };

	var init = function () {
		dateInput.datepicker(defaultDatePickerOptions);
		timeInput.timepicker({showMeridian: false});

		registerEvents();
	};

	var registerEvents = function () {
		dateInput.datepicker().on('changeDate', function(ev){
			if (!ev.date) { // invalid date set
				return;
			}
			dateHiddenValue.val(formatDateInternal(ev.date));
			dateInput.datepicker("hide");
			if (!timeManualCheckbox.is(':checked')) {
				setTime(autoTime);
			}
			triggerChangeEvent();
		});
		timeManualCheckbox.on('change', function(ev) {
			var manualSelection = timeManualCheckbox.is(':checked');
			timeInput.attr("disabled", !manualSelection);
			triggerChangeEvent();
		});
		timeInput.on('changeTime.timepicker', function(ev) {
			timeHiddenValue.val(ev.time.value);
			triggerChangeEvent();
		});
	};

	var setManualTime = function(isManualTime) {
        timeManualCheckbox.prop('checked', isManualTime);
        timeInput.attr("disabled", !isManualTime);
    };

	var setTime = function(time) {
		var pattern = new RegExp("([0-9]|[01][0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?");
		if (!pattern.test(time)) {
			console.log("Invalid time to set. Expected in format hh:mm");
			time = autoTime;
		}

		timeInput.val(time);
		timeHiddenValue.val(time);
		var timePickerValue = time;

		// workaround for the bootstrap timepicker
		if (timePickerValue == '00:00' || timePickerValue == '0:00') {
			timePickerValue = "00:001";
		}

		timeInput.timepicker('setTime', timePickerValue);
	};

	var setDate = function (date) {
		var dateObject = parseDateInternal(date);
		dateHiddenValue.val(date);
		dateInput.datepicker("setDate", dateObject);
	};

	var parseDateInternal = function (dateString) {
		var pattern = new RegExp("([0-2][0-9]|3[01]).(0[1-9]|1[0-2]).[0-9]{4}");
		if (!pattern.test(dateString)) {
			console.log("Invalid date to set. Expected in format dd.mm.yyyy");
			return new Date();
		}
		var parts = dateString.split(".");
		return new Date(parts[2], parts[1] - 1, parts[0]);
	};

	var formatDateInternal = function(date) {
		return twoDigitString(date.getDate()) + "." + twoDigitString(date.getMonth() + 1) + "." + date.getFullYear();
	};

	var twoDigitString = function(number) {
		return ("00" + number).substr(-2,2);
	};

	var triggerChangeEvent = function() {
		dateTimePickerElement.trigger("changeDateTime", getValues());
	};

	var setValuesByDate= function(date) {
        if (!(date instanceof Date)) {
            console.log("Invalid date object to set datepicker from.");
            return;
        }
        setDate(formatDateInternal(date));
        setTime(twoDigitString(date.getHours()) + ":" + twoDigitString(date.getMinutes()));
    };

	var getValues = function() {
        return {
            date: dateHiddenValue.val(),
            manualTime: timeManualCheckbox.is(':checked'),
            time: timeHiddenValue.val()
        };
    };

    var getValuesAsDate = function() {
		var values = getValues();
		var date = parseDateInternal(values.date);
		var timeString = (values.manualTime && values.time) ? values.time : autoTime;
		var timeParts = timeString.split(":");
		date.setHours(timeParts[0]);
		date.setMinutes(timeParts[1]);
		if (timeParts[2]) {
			date.setSeconds(timeParts[2]);
		}
		return date;
	};

    var setValues = function (newValues) {
        if (newValues.date) {
            setDate(newValues.date);
        }
		if (newValues.manualTime !== undefined) {
			var isManual = typeof newValues.manualTime == "string" ?
                OpenSpeedMonitor.stringUtils.stringToBoolean(newValues.manualTime) : newValues.manualTime;
			setManualTime(isManual);
		}
		if (newValues.time) {
			setTime(newValues.time);
		}
	};

	var setStartDate = function(startDate) {
		var dateObject = null;
        if (startDate instanceof Date) {
            dateObject = startDate;
        } else if (startDate) {
            dateObject = parseDateInternal(startDate);
        }
		dateInput.datepicker("setStartDate", dateObject);
	};

	init();
	return {
		getValues: getValues,
		getValuesAsDate: getValuesAsDate,
        setValues: setValues,
        setValuesByDate: setValuesByDate,
		setStartDate: setStartDate
	};
};
<commit_msg>whitespace: Converted tabs to spaces<commit_after>/*
* OpenSpeedMonitor (OSM)
* Copyright 2014 iteratec GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

"use strict";

var OpenSpeedMonitor = OpenSpeedMonitor || {};

OpenSpeedMonitor.DateTimePicker = function(dateTimePickerElement, autoTime) {
    dateTimePickerElement = $(dateTimePickerElement);
    autoTime = autoTime || "00:00";

    var dateInput = dateTimePickerElement.find(".date-control input[type='text']");
    var timeInput = dateTimePickerElement.find(".time-control input[type='text']");
    var timeManualCheckbox = dateTimePickerElement.find(".time-control input[type='checkbox']");
    var dateHiddenValue = dateTimePickerElement.find("input.date-hidden");
    var timeHiddenValue = dateTimePickerElement.find("input.time-hidden");
    var defaultDatePickerOptions = { endDate: "+0d" };

    var init = function () {
        dateInput.datepicker(defaultDatePickerOptions);
        timeInput.timepicker({showMeridian: false});

        registerEvents();
    };

    var registerEvents = function () {
        dateInput.datepicker().on('changeDate', function(ev){
            if (!ev.date) { // invalid date set
                return;
            }
            dateHiddenValue.val(formatDateInternal(ev.date));
            dateInput.datepicker("hide");
            if (!timeManualCheckbox.is(':checked')) {
                setTime(autoTime);
            }
            triggerChangeEvent();
        });
        timeManualCheckbox.on('change', function(ev) {
            var manualSelection = timeManualCheckbox.is(':checked');
            timeInput.attr("disabled", !manualSelection);
            triggerChangeEvent();
        });
        timeInput.on('changeTime.timepicker', function(ev) {
            timeHiddenValue.val(ev.time.value);
            triggerChangeEvent();
        });
    };

    var setManualTime = function(isManualTime) {
        timeManualCheckbox.prop('checked', isManualTime);
        timeInput.attr("disabled", !isManualTime);
    };

    var setTime = function(time) {
        var pattern = new RegExp("([0-9]|[01][0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?");
        if (!pattern.test(time)) {
            console.log("Invalid time to set. Expected in format hh:mm");
            time = autoTime;
        }

        timeInput.val(time);
        timeHiddenValue.val(time);
        var timePickerValue = time;

        // workaround for the bootstrap timepicker
        if (timePickerValue == '00:00' || timePickerValue == '0:00') {
            timePickerValue = "00:001";
        }

        timeInput.timepicker('setTime', timePickerValue);
    };

    var setDate = function (date) {
        var dateObject = parseDateInternal(date);
        dateHiddenValue.val(date);
        dateInput.datepicker("setDate", dateObject);
    };

    var parseDateInternal = function (dateString) {
        var pattern = new RegExp("([0-2][0-9]|3[01]).(0[1-9]|1[0-2]).[0-9]{4}");
        if (!pattern.test(dateString)) {
            console.log("Invalid date to set. Expected in format dd.mm.yyyy");
            return new Date();
        }
        var parts = dateString.split(".");
        return new Date(parts[2], parts[1] - 1, parts[0]);
    };

    var formatDateInternal = function(date) {
        return twoDigitString(date.getDate()) + "." + twoDigitString(date.getMonth() + 1) + "." + date.getFullYear();
    };

    var twoDigitString = function(number) {
        return ("00" + number).substr(-2,2);
    };

    var triggerChangeEvent = function() {
        dateTimePickerElement.trigger("changeDateTime", getValues());
    };

    var setValuesByDate= function(date) {
        if (!(date instanceof Date)) {
            console.log("Invalid date object to set datepicker from.");
            return;
        }
        setDate(formatDateInternal(date));
        setTime(twoDigitString(date.getHours()) + ":" + twoDigitString(date.getMinutes()));
    };

    var getValues = function() {
        return {
            date: dateHiddenValue.val(),
            manualTime: timeManualCheckbox.is(':checked'),
            time: timeHiddenValue.val()
        };
    };

    var getValuesAsDate = function() {
        var values = getValues();
        var date = parseDateInternal(values.date);
        var timeString = (values.manualTime && values.time) ? values.time : autoTime;
        var timeParts = timeString.split(":");
        date.setHours(timeParts[0]);
        date.setMinutes(timeParts[1]);
        if (timeParts[2]) {
            date.setSeconds(timeParts[2]);
        }
        return date;
    };

    var setValues = function (newValues) {
        if (newValues.date) {
            setDate(newValues.date);
        }
        if (newValues.manualTime !== undefined) {
            var isManual = typeof newValues.manualTime == "string" ?
                OpenSpeedMonitor.stringUtils.stringToBoolean(newValues.manualTime) : newValues.manualTime;
            setManualTime(isManual);
        }
        if (newValues.time) {
            setTime(newValues.time);
        }
    };

    var setStartDate = function(startDate) {
        var dateObject = null;
        if (startDate instanceof Date) {
            dateObject = startDate;
        } else if (startDate) {
            dateObject = parseDateInternal(startDate);
        }
        dateInput.datepicker("setStartDate", dateObject);
    };

    init();
    return {
        getValues: getValues,
        getValuesAsDate: getValuesAsDate,
        setValues: setValues,
        setValuesByDate: setValuesByDate,
        setStartDate: setStartDate
    };
};
<|endoftext|>


<commit_before>var tmonth=new Array("JAN","FEB","MAR","APR","May","JUN","JUL","AUG","SEPT","OCT","NOV","DEC");
var images=new Array("https://rc4.sg/img/bkg1.jpg","https://rc4.sg/img/bkg3.jpg","https://graphipaintartographer.files.wordpress.com/2011/08/3508-11jmg-blws-posters-landscape-2-2-small.jpg");
var curImage = 0;

moment.locale('en', {
    relativeTime : {
        future: "in %s",
        s:  "Arr",
        m:  "1 min",
        mm: "%d mins"
    }
});

function GetClock(){
	var d=new Date();
	var nmonth=d.getMonth(),ndate=d.getDate(),nyear=d.getYear();
	if(nyear<1000) nyear+=1900;

	var d=new Date();
	var nhour=d.getHours(),nmin=d.getMinutes(),nsec=d.getSeconds();
	if(nmin<=9) nmin="0"+nmin
	if(nsec<=9) nsec="0"+nsec;

	document.getElementById('clockbox').innerHTML=""+ndate+" "+tmonth[nmonth]+"
"+nhour+":"+nmin+":"+nsec+"";
}

function loadNextImage(){
	$('#curImage').fadeOut(500, loadNextImageCompleted);
}

function loadNextImageCompleted(){
	if(++curImage >= images.length)
		curImage = 0;

	$('#curImage')[0].src=images[curImage];
	$('#curImage').fadeIn(500);
}

function getBusTiming(){
  $.ajax({
    url: 'https://query.yahooapis.com/v1/public/yql',
    data: {
        q: "select * from json where url ='https://myaces.nus.edu.sg/prjbus/services/shuttlebus/UTOWN'",
        format: "json"
    },
    dataType: "jsonp",
    success: function (data) {
        isb = data.query.results.json.shuttles;
        for(var i=0; i<isb.length; i++) {
	        if(isb[i].name=="D1 (UTown)") {
	            if(isb[i].arrivalTime != "Arr" && isb[i].arrivalTime != "-")
	            	$('#rc4_d1').html(isb[i].arrivalTime + " mins");
	            else
	            	$('#rc4_d1').html(isb[i].arrivalTime);
	        }
	        else if (isb[i].name=="D2") {
	        	if(isb[i].arrivalTime != "Arr" && isb[i].arrivalTime != "-")
	            	$('#rc4_d2').html(isb[i].arrivalTime + " mins");
	            else
	            	$('#rc4_d2').html(isb[i].arrivalTime);
	        }
	    }

    }
});

  $.ajax({
    url: 'https://arrivelah.herokuapp.com/?id=19059',
    dataType: "json",
    success: function (data) {
        //console.log(JSON.stringify(data));
        lta = data.services;
        for(var i=0; i<lta.length; i++)
        {
        	if(lta[i].no == "33")
            	$('#lta_33').html(moment(lta[i]["next"]["time"]).fromNow(true));
            else if(lta[i].no == "196")
            	$('#lta_196').html(moment(lta[i]["next"]["time"]).fromNow(true));
          //lta[i]["subsequent"]["time"] = moment(lta[i]["subsequent"]["time"]).fromNow(true);
        }
    }
});

    $.ajax({
    url: 'https://arrivelah.herokuapp.com/?id=19051',
    dataType: "json",
    success: function (data) {
        //console.log(JSON.stringify(data));
        lta = data.services;
        for(var i=0; i<lta.length; i++)
        {
            if(lta[i].no == "33")
            	$('#lta_opp_33').html(moment(lta[i]["next"]["time"]).fromNow(true));
            else if(lta[i].no == "196")
            	$('#lta_opp_196').html(moment(lta[i]["next"]["time"]).fromNow(true));
        }

    }
});
}

window.onload=function(){
	GetClock();
	getBusTiming();
	loadNextImageCompleted();
	setInterval(GetClock,1000);
	setInterval(loadNextImage,6000);
	setInterval(getBusTiming,30000);
}<commit_msg>Added 'no bus timing' text<commit_after>var tmonth=new Array("JAN","FEB","MAR","APR","May","JUN","JUL","AUG","SEPT","OCT","NOV","DEC");
var images=new Array("https://rc4.sg/img/bkg1.jpg","https://rc4.sg/img/bkg3.jpg","https://graphipaintartographer.files.wordpress.com/2011/08/3508-11jmg-blws-posters-landscape-2-2-small.jpg");
var curImage = 0;

moment.locale('en', {
    relativeTime : {
        future: "in %s",
        s:  "Arr",
        m:  "1 min",
        mm: "%d mins"
    },
    invalidDate: "-"
});

function GetClock(){
	var d=new Date();
	var nmonth=d.getMonth(),ndate=d.getDate(),nyear=d.getYear();
	if(nyear<1000) nyear+=1900;

	var d=new Date();
	var nhour=d.getHours(),nmin=d.getMinutes(),nsec=d.getSeconds();
	if(nmin<=9) nmin="0"+nmin
	if(nsec<=9) nsec="0"+nsec;

	document.getElementById('clockbox').innerHTML=""+ndate+" "+tmonth[nmonth]+"
"+nhour+":"+nmin+":"+nsec+"";
}

function loadNextImage(){
	$('#curImage').fadeOut(500, loadNextImageCompleted);
}

function loadNextImageCompleted(){
	if(++curImage >= images.length)
		curImage = 0;

	$('#curImage')[0].src=images[curImage];
	$('#curImage').fadeIn(500);
}

function getBusTiming(){
  $.ajax({
    url: 'https://query.yahooapis.com/v1/public/yql',
    data: {
        q: "select * from json where url ='https://myaces.nus.edu.sg/prjbus/services/shuttlebus/UTOWN'",
        format: "json"
    },
    dataType: "jsonp",
    success: function (data) {
        isb = data.query.results.json.shuttles;
        for(var i=0; i<isb.length; i++) {
	        if(isb[i].name=="D1 (UTown)") {
	            if(isb[i].arrivalTime != "Arr" && isb[i].arrivalTime != "-")
	            	$('#rc4_d1').html(isb[i].arrivalTime + " mins");
	            else
	            	$('#rc4_d1').html(isb[i].arrivalTime);
	        }
	        else if (isb[i].name=="D2") {
	        	if(isb[i].arrivalTime != "Arr" && isb[i].arrivalTime != "-")
	            	$('#rc4_d2').html(isb[i].arrivalTime + " mins");
	            else
	            	$('#rc4_d2').html(isb[i].arrivalTime);
	        }
	    }

    }
});

  $.ajax({
    url: 'https://arrivelah.herokuapp.com/?id=19059',
    dataType: "json",
    success: function (data) {
        //console.log(JSON.stringify(data));
        lta = data.services;
        for(var i=0; i<lta.length; i++)
        {
        	if(lta[i].no == "33")
            	$('#lta_33').html(moment(lta[i]["next"]["time"]).fromNow(true));
            else if(lta[i].no == "196")
            	$('#lta_196').html(moment(lta[i]["next"]["time"]).fromNow(true));
          //lta[i]["subsequent"]["time"] = moment(lta[i]["subsequent"]["time"]).fromNow(true);
        }
    }
});

    $.ajax({
    url: 'https://arrivelah.herokuapp.com/?id=19051',
    dataType: "json",
    success: function (data) {
        //console.log(JSON.stringify(data));
        lta = data.services;
        for(var i=0; i<lta.length; i++)
        {
            if(lta[i].no == "33")
            	$('#lta_opp_33').html(moment(lta[i]["next"]["time"]).fromNow(true));
            else if(lta[i].no == "196")
            	$('#lta_opp_196').html(moment(lta[i]["next"]["time"]).fromNow(true));
        }

    }
});
}

window.onload=function(){
	GetClock();
	getBusTiming();
	loadNextImageCompleted();
	setInterval(GetClock,1000);
	setInterval(loadNextImage,6000);
	setInterval(getBusTiming,30000);
}<|endoftext|>


<commit_before>/**
 * @file MaterialTextArea component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import TextArea from '../TextArea';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialTextArea extends Component {

    static Type = TextArea.Type;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.triggerBlurHandler = ::this.triggerBlurHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        this.setState({
            isFocus: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimate, wordCountVisible,
                ...restProps
            } = this.props,
            {isFocus, isHover, value} = this.state,

            wrapperClassName = (isLabelAnimate ? ' animated' : '') + (label ? ' has-label' : '')
                + (isFocus ? ' focused' : '') + (value ? ' has-value' : '')
                + (wordCountVisible ? ' has-word-count' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <TextArea {...restProps}
                          theme={theme}
                          value={value}
                          wordCountVisible={wordCountVisible}
                          onFocus={this.triggerFocusHandler}
                          onBlur={this.triggerBlurHandler}
                          onMouseOver={this.triggerMouseOverHandler}
                          onMouseOut={this.triggerMouseOutHandler}
                          onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialTextArea.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The TextArea theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Specifies the type of input to display such as "password" or "text".
     */
    type: PropTypes.oneOf(Util.enumerateValue(TextArea.Type)),

    /**
     * The name of the text area.
     */
    name: PropTypes.string,

    /**
     * The label of the text field.
     */
    label: PropTypes.any,

    /**
     * The animate of the text field.
     */
    isLabelAnimate: PropTypes.bool,

    /**
     * The placeholder of the text area.
     */
    placeholder: PropTypes.string,

    /**
     * The value of the text area. Type can be string or number.
     */
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Disables the textField if set to true.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the textField will can't editable.
     */
    readOnly: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The message of the textField.
     */
    infoMsg: PropTypes.string,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * If true, passwordButton will display.
     */
    passwordButtonVisible: PropTypes.bool,

    autoHeight: PropTypes.bool,

    wordCountVisible: PropTypes.bool,

    // valid
    /**
     * If true,the textField must be required.
     */
    required: PropTypes.bool,

    /**
     * The maximum length of textField to enter.
     */
    maxLength: PropTypes.number,

    /**
     * Maximum number of textField that can be entered.
     */
    max: PropTypes.number,

    /**
     * Minimum number of textField that can be entered.
     */
    min: PropTypes.number,

    /**
     * The matching rule of textField.
     */
    pattern: PropTypes.object,

    /**
     * The invalid message of textField.
     */
    patternInvalidMsg: PropTypes.string,

    fieldMsgVisible: PropTypes.bool,

    /**
     * Callback function fired when the textField is changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when the press enter.
     */
    onPressEnter: PropTypes.func,

    /**
     * Callback function fired when textField valid.
     */
    onValid: PropTypes.func,

    /**
     * Callback function fired when textField invalid.
     */
    onInvalid: PropTypes.func,

    /**
     * Callback function fired when textField focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when textField blur.
     */
    onBlur: PropTypes.func,

    /**
     * Callback function fired when textField clear.
     */
    onClear: PropTypes.func,

    /**
     * Callback function fired when password visible.
     */
    onPasswordVisible: PropTypes.func,

    /**
     * Callback function fired when password invisible.
     */
    onPasswordInvisible: PropTypes.func,

    onIconTouchTap: PropTypes.func,
    onRightIconTouchTap: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

MaterialTextArea.defaultProps = {

    className: '',
    style: null,
    theme: Theme.DEFAULT,

    type: 'text',
    name: '',
    label: '',
    isLabelAnimate: true,
    placeholder: '',
    value: '',
    iconCls: '',
    rightIconCls: '',
    disabled: false,
    readOnly: false,
    autoFocus: false,
    infoMsg: '',
    autoHeight: false,
    wordCountVisible: false,

    clearButtonVisible: false,
    passwordButtonVisible: false,

    // valid
    required: false,
    patternInvalidMsg: '',
    preventInvalidInput: false,

    fieldMsgVisible: false

};<commit_msg>update MaterialTextArea<commit_after>/**
 * @file MaterialTextArea component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import TextArea from '../TextArea';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

class MaterialTextArea extends Component {

    static Type = TextArea.Type;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.triggerBlurHandler = ::this.triggerBlurHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        this.setState({
            isFocus: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimate, wordCountVisible,
                ...restProps
            } = this.props,
            {isFocus, isHover, value} = this.state,

            wrapperClassName = (isLabelAnimate ? ' animated' : '') + (label ? ' has-label' : '')
                + (isFocus ? ' focused' : '') + (value ? ' has-value' : '')
                + (wordCountVisible ? ' has-word-count' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <TextArea {...restProps}
                          theme={theme}
                          value={value}
                          wordCountVisible={wordCountVisible}
                          onFocus={this.triggerFocusHandler}
                          onBlur={this.triggerBlurHandler}
                          onMouseOver={this.triggerMouseOverHandler}
                          onMouseOut={this.triggerMouseOutHandler}
                          onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialTextArea.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The TextArea theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Specifies the type of input to display such as "password" or "text".
     */
    type: PropTypes.oneOf(Util.enumerateValue(TextArea.Type)),

    /**
     * The name of the text area.
     */
    name: PropTypes.string,

    /**
     * The label of the text field.
     */
    label: PropTypes.any,

    /**
     * The animate of the text field.
     */
    isLabelAnimate: PropTypes.bool,

    /**
     * The placeholder of the text area.
     */
    placeholder: PropTypes.string,

    /**
     * The value of the text area. Type can be string or number.
     */
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Disables the textField if set to true.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the textField will can't editable.
     */
    readOnly: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The message of the textField.
     */
    infoMsg: PropTypes.string,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * If true, passwordButton will display.
     */
    passwordButtonVisible: PropTypes.bool,

    autoHeight: PropTypes.bool,

    wordCountVisible: PropTypes.bool,

    // valid
    /**
     * If true,the textField must be required.
     */
    required: PropTypes.bool,

    /**
     * The maximum length of textField to enter.
     */
    maxLength: PropTypes.number,

    /**
     * Maximum number of textField that can be entered.
     */
    max: PropTypes.number,

    /**
     * Minimum number of textField that can be entered.
     */
    min: PropTypes.number,

    /**
     * The matching rule of textField.
     */
    pattern: PropTypes.object,

    /**
     * The invalid message of textField.
     */
    patternInvalidMsg: PropTypes.string,

    fieldMsgVisible: PropTypes.bool,

    /**
     * Callback function fired when the textField is changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when the press enter.
     */
    onPressEnter: PropTypes.func,

    /**
     * Callback function fired when textField valid.
     */
    onValid: PropTypes.func,

    /**
     * Callback function fired when textField invalid.
     */
    onInvalid: PropTypes.func,

    /**
     * Callback function fired when textField focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when textField blur.
     */
    onBlur: PropTypes.func,

    /**
     * Callback function fired when textField clear.
     */
    onClear: PropTypes.func,

    /**
     * Callback function fired when password visible.
     */
    onPasswordVisible: PropTypes.func,

    /**
     * Callback function fired when password invisible.
     */
    onPasswordInvisible: PropTypes.func,

    onIconTouchTap: PropTypes.func,
    onRightIconTouchTap: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

MaterialTextArea.defaultProps = {

    className: '',
    style: null,
    theme: Theme.DEFAULT,

    type: 'text',
    name: '',
    label: '',
    isLabelAnimate: true,
    placeholder: '',
    value: '',
    iconCls: '',
    rightIconCls: '',
    disabled: false,
    readOnly: false,
    autoFocus: false,
    infoMsg: '',
    autoHeight: false,
    wordCountVisible: false,

    clearButtonVisible: false,
    passwordButtonVisible: false,

    // valid
    required: false,
    patternInvalidMsg: '',
    preventInvalidInput: false,

    fieldMsgVisible: false

};

export default MaterialTextArea;<|endoftext|>


<commit_before>var utils = require('./test-utils');

var assert = require('chai').assert;
var path = require('path');
var crypto = require('crypto');

var FileStorage = require('../lib/database/storage').FileStorage;

function sha1hash(string) {
  var shasum = crypto.createHash('sha1');
  shasum.update(string);
  return shasum.digest('hex');
}

function createNewDocument() {
  return {
    id: Date.now(),
    date: (new Date()).toString()
  };
}

function assertFilesCount(directoryPath, delta, task) {
  var files = fs.readdirSync(directoryPath);
  var beforeCount = files.length;
  task();
  files = fs.readdirSync(directoryPath);
  var afterCount = files.length;
  assert.equal(afterCount - beforeCount, delta);
}

suite('database', function() {
  suite('FileStorage', function() {
    var storage;
    setup(function() {
      storage = new FileStorage({ name:     'storage',
                                  basePath: utils.temporaryDirectory });
    });

    teardown(function() {
      storage.clearSync();
      storage = undefined;
    });

    test('saveSync for new document', function() {
      var document = createNewDocument();
      var filePath = path.join(utils.temporaryDirectory, 'storage', sha1hash(document.id));
      assert.isFalse(path.existsSync(filePath));

      assertFilesCount(storage.directoryPath, 1, function() {
        storage.saveSync(document);
      });

      assert.isTrue(path.existsSync(filePath));

      var contents = fs.readFileSync(filePath, 'UTF-8');
      contents = JSON.parse(contents);
      assert.deepEqual(contents, document);
    });

    test('saveSync for existing document', function() {
      var document = createNewDocument();
      storage.saveSync(document);

      var updatedDocument = createNewDocument();
      updatedDocument.id = document.id;
      assertFilesCount(storage.directoryPath, 0, function() {
        storage.saveSync(updatedDocument);
      });

      var contents = fs.readFileSync(filePath, 'UTF-8');
      contents = JSON.parse(contents);
      assert.deepEqual(contents, updatedDocument);
    });

    test('readSync for existing document', function() {
      var document = createNewDocument();
      storage.saveSync(document);

      var readDocument = storage.readSync(document.id);
      assert.deepEqual(readDocument, document);
    });

    test('readSync for unknown document', function() {
      var readDocument = storage.readSync('unknown ' + Date.now());
      assert.equal(readDocument, null);
    });

    test('deleteSync for existing document', function() {
      var document = createNewDocument();
      storage.saveSync(document);

      var filePath = path.join(utils.temporaryDirectory, 'storage', sha1hash(document.id));
      assert.isTrue(path.existsSync(filePath));

      assertFilesCount(storage.directoryPath, -1, function() {
        storage.deleteSync(document.id);
        assert.isFalse(path.existsSync(filePath));
      });
    });

    test('deleteSync for unknown document', function() {
      assertFilesCount(storage.directoryPath, 0, function() {
        storage.deleteSync('unknown ' + Date.now());
      });
    });

    test('clearSync', function() {
      assertFilesCount(storage.directoryPath, 3, function() {
        storage.saveSync(createNewDocument());
        storage.saveSync(createNewDocument());
        storage.saveSync(createNewDocument());
      });
      storage.clearSync();
      assert.isFalse(path.existsSync(storage.directoryPath));
    });
  });
});
<commit_msg>Forgot to load fs<commit_after>var utils = require('./test-utils');

var assert = require('chai').assert;
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');

var FileStorage = require('../lib/database/storage').FileStorage;

function sha1hash(string) {
  var shasum = crypto.createHash('sha1');
  shasum.update(string);
  return shasum.digest('hex');
}

function createNewDocument() {
  return {
    id: Date.now(),
    date: (new Date()).toString()
  };
}

function assertFilesCount(directoryPath, delta, task) {
  var files = fs.readdirSync(directoryPath);
  var beforeCount = files.length;
  task();
  files = fs.readdirSync(directoryPath);
  var afterCount = files.length;
  assert.equal(afterCount - beforeCount, delta);
}

suite('database', function() {
  suite('FileStorage', function() {
    var storage;
    setup(function() {
      storage = new FileStorage({ name:     'storage',
                                  basePath: utils.temporaryDirectory });
    });

    teardown(function() {
      storage.clearSync();
      storage = undefined;
    });

    test('saveSync for new document', function() {
      var document = createNewDocument();
      var filePath = path.join(utils.temporaryDirectory, 'storage', sha1hash(document.id));
      assert.isFalse(path.existsSync(filePath));

      assertFilesCount(storage.directoryPath, 1, function() {
        storage.saveSync(document);
      });

      assert.isTrue(path.existsSync(filePath));

      var contents = fs.readFileSync(filePath, 'UTF-8');
      contents = JSON.parse(contents);
      assert.deepEqual(contents, document);
    });

    test('saveSync for existing document', function() {
      var document = createNewDocument();
      storage.saveSync(document);

      var updatedDocument = createNewDocument();
      updatedDocument.id = document.id;
      assertFilesCount(storage.directoryPath, 0, function() {
        storage.saveSync(updatedDocument);
      });

      var contents = fs.readFileSync(filePath, 'UTF-8');
      contents = JSON.parse(contents);
      assert.deepEqual(contents, updatedDocument);
    });

    test('readSync for existing document', function() {
      var document = createNewDocument();
      storage.saveSync(document);

      var readDocument = storage.readSync(document.id);
      assert.deepEqual(readDocument, document);
    });

    test('readSync for unknown document', function() {
      var readDocument = storage.readSync('unknown ' + Date.now());
      assert.equal(readDocument, null);
    });

    test('deleteSync for existing document', function() {
      var document = createNewDocument();
      storage.saveSync(document);

      var filePath = path.join(utils.temporaryDirectory, 'storage', sha1hash(document.id));
      assert.isTrue(path.existsSync(filePath));

      assertFilesCount(storage.directoryPath, -1, function() {
        storage.deleteSync(document.id);
        assert.isFalse(path.existsSync(filePath));
      });
    });

    test('deleteSync for unknown document', function() {
      assertFilesCount(storage.directoryPath, 0, function() {
        storage.deleteSync('unknown ' + Date.now());
      });
    });

    test('clearSync', function() {
      assertFilesCount(storage.directoryPath, 3, function() {
        storage.saveSync(createNewDocument());
        storage.saveSync(createNewDocument());
        storage.saveSync(createNewDocument());
      });
      storage.clearSync();
      assert.isFalse(path.existsSync(storage.directoryPath));
    });
  });
});
<|endoftext|>


<commit_before>/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
    /**
     * Encodes the keys and values based on their types. This is required to maintain collations
     */
    var collations = ["", "number", "string", "boolean", "object", "undefined"];
    var getGenericEncoder = function(){
        return {
            "encode": function(key){
                return collations.indexOf(typeof key) + "-" + JSON.stringify(key);
            },
            "decode": function(key){
                if (typeof key === "undefined") {
                    return undefined;
                }
                else {
                    return JSON.parse(key.substring(2));
                }
            }
        };
    };

    var types = {
        "number": getGenericEncoder("number"), // decoder will fail for NaN
        "boolean": getGenericEncoder(),
        "object": getGenericEncoder(),
        "string": {
            "encode": function(key){
                return collations.indexOf("string") + "-" + key;
            },
            "decode": function(key){
                return "" + key.substring(2);
            }
        },
        "undefined": {
            "encode": function(key){
                return collations.indexOf("undefined") + "-undefined";
            },
            "decode": function(key){
                return undefined;
            }
        }
    };

    var Key = (function(){
        return {
            encode: function(key){
                return types[typeof key].encode(key);
            },
            decode: function(key){
                return types[collations[key.substring(0, 1)]].decode(key);
            },
            evalKeyPath: function (keyPath, value) {
                if (Object.prototype.toString.apply(keyPath) === '[object Array]') {
                    var keyValue = [];
                    for (var i = 0; i < keyPath.length; i++) {
                        keyValue.push(eval("value['" + keyPath[i] + "']"));
                    }
                    return keyValue;
                } else {
                    return eval("value['" + keyPath + "']");
                }
            }
        };
    }());
    idbModules.Key = Key;
}(idbModules));
<commit_msg>add support for more complex inline keys<commit_after>/*jshint globalstrict: true*/
'use strict';
(function(idbModules){
    /**
     * Encodes the keys and values based on their types. This is required to maintain collations
     */
    var collations = ["", "number", "string", "boolean", "object", "undefined"];
    var getGenericEncoder = function(){
        return {
            "encode": function(key){
                return collations.indexOf(typeof key) + "-" + JSON.stringify(key);
            },
            "decode": function(key){
                if (typeof key === "undefined") {
                    return undefined;
                }
                else {
                    return JSON.parse(key.substring(2));
                }
            }
        };
    };

    var types = {
        "number": getGenericEncoder("number"), // decoder will fail for NaN
        "boolean": getGenericEncoder(),
        "object": getGenericEncoder(),
        "string": {
            "encode": function(key){
                return collations.indexOf("string") + "-" + key;
            },
            "decode": function(key){
                return "" + key.substring(2);
            }
        },
        "undefined": {
            "encode": function(key){
                return collations.indexOf("undefined") + "-undefined";
            },
            "decode": function(key){
                return undefined;
            }
        }
    };

    function evalComplexKeyPath(keyPath, value) {
        var result, segments = keyPath.split(".");

        if (segments.length === 2) {
            // NOTE: only supporting shallow object keys
            result = eval("value['" + segments[0] + "'] && value['" + segments[0] + "']['" + segments[1] + "']");
        } else {
            result = eval("value['" + segments[0] + "']");
        }
        return result;
    }

    var Key = (function(){
        return {
            encode: function(key){
                return types[typeof key].encode(key);
            },
            decode: function(key){
                return types[collations[key.substring(0, 1)]].decode(key);
            },
            evalKeyPath: function (keyPath, value) {
                if (Object.prototype.toString.apply(keyPath) === '[object Array]') {
                    var keyValue = [];
                    for (var i = 0; i < keyPath.length; i++) {
                        keyValue.push(evalComplexKeyPath(keyPath[i], value));
                    }
                    return keyValue;
                } else {
                    return evalComplexKeyPath(keyPath, value);
                }
            }
        };
    }());
    idbModules.Key = Key;
}(idbModules));
<|endoftext|>


<commit_before>var Vector3 = function(x, y, z) {
    if (typeof(x) === "number") {
        this.x = x;
        this.y = y;
        this.z = z || 0.0;
    }
    else {
        this.x = x.x || x[0];
        this.y = x.y || x[1];
        this.z = x.z || x[2] || 0.0;
    }

    this.toPoint = function() {
        return L.point(this.x, this.y);
    };
};

var Vector4 = function(x, y, z, w) {
    if (typeof(x) === "number") {
        this.x = x;
        this.y = y;
        this.z = z || 0.0;
        this.w = w || 1.0;
    }
    else {
        this.x = x.x || x[0];
        this.y = x.y || x[1];
        this.z = x.z || x[2] || 0.0;
        this.w = x.w || x[3] || 1.0;
    }
};

// TODO: fix DRY fail -- this should move to platform
// Judging by Leaflet, approx = 2.74287e7 * Math.exp(-0.622331 * zoom)
var _altitudes = [
    27428700,
    14720762,
    8000000,
    4512909,
    2087317,
    1248854,
    660556,
    351205,
    185652,
    83092,
    41899,
    21377,
    11294,
    5818,
    3106,
    1890,
    1300,
    821,
    500,
    300,
    108,
    58,
    31,
    17,
    9,
    5
];

var _lerp = function (a,  b,  c) {
    return a + c * (b - a);
}

var _altitudeToZoom = function(altitude, comparisonFunc) {
    var zoom = _altitudes.findIndex(function(zoomLevelDistance) {
        return comparisonFunc(altitude, zoomLevelDistance);
    });
    var maxZoom = _altitudes.length - 1;
    return (zoom === -1) ? maxZoom : zoom;
};

var _nearestZoomAbove = function(distance) {
    var zoomAbove = _altitudeToZoom(distance, function(d, z) { return d > z; });
    return Math.max(0, zoomAbove - 1);
};

var _nearestZoomBelow = function(distance) {
    return _altitudeToZoom(distance, function(d, z) { return d >= z; });
};

var zoomToDistance = function(zoom) {
    var zoomlevel = zoom;
    if(zoomlevel < 0) {
        zoomlevel =  0;
    }
    else if (zoomlevel >= _altitudes.length) {
        zoomlevel = _altitudes.length - 1;
    }

    var nearestZoomBelow = Math.floor(zoomlevel);
    var nearestZoomAbove = Math.ceil(zoomlevel);
    var valueBetweenNearestZoomLevels = zoomlevel - nearestZoomBelow;
    return _lerp(_altitudes[nearestZoomBelow], _altitudes[nearestZoomAbove], valueBetweenNearestZoomLevels);
};

var distanceToZoom = function(distance) {
    var smallestAltitude = _altitudes.length - 1;
    if(distance < _altitudes[smallestAltitude])
    {
        distance = _altitudes[smallestAltitude];
    }
    if(distance > _altitudes[0])
    {
        distance = _altitudes[0];
    }
    var nearestZoomAbove = _nearestZoomAbove(distance);
    var nearestZoomBelow = _nearestZoomBelow(distance);
    var distanceBetweenNearestZoomLevels = _altitudes[nearestZoomAbove] - _altitudes[nearestZoomBelow];
    var distanceFromZoomLevelBelow = distance - _altitudes[nearestZoomBelow];

    return (nearestZoomAbove === nearestZoomBelow) ? nearestZoomBelow : _lerp(nearestZoomBelow, nearestZoomAbove, distanceFromZoomLevelBelow / distanceBetweenNearestZoomLevels);
};

var space = {
    Vector3: Vector3,
    Vector4: Vector4,
    zoomToDistance: zoomToDistance,
    distanceToZoom: distanceToZoom
};

module.exports = space;<commit_msg>Fixing lint error. Buddy: Calum L<commit_after>var Vector3 = function(x, y, z) {
    if (typeof(x) === "number") {
        this.x = x;
        this.y = y;
        this.z = z || 0.0;
    }
    else {
        this.x = x.x || x[0];
        this.y = x.y || x[1];
        this.z = x.z || x[2] || 0.0;
    }

    this.toPoint = function() {
        return L.point(this.x, this.y);
    };
};

var Vector4 = function(x, y, z, w) {
    if (typeof(x) === "number") {
        this.x = x;
        this.y = y;
        this.z = z || 0.0;
        this.w = w || 1.0;
    }
    else {
        this.x = x.x || x[0];
        this.y = x.y || x[1];
        this.z = x.z || x[2] || 0.0;
        this.w = x.w || x[3] || 1.0;
    }
};

// TODO: fix DRY fail -- this should move to platform
// Judging by Leaflet, approx = 2.74287e7 * Math.exp(-0.622331 * zoom)
var _altitudes = [
    27428700,
    14720762,
    8000000,
    4512909,
    2087317,
    1248854,
    660556,
    351205,
    185652,
    83092,
    41899,
    21377,
    11294,
    5818,
    3106,
    1890,
    1300,
    821,
    500,
    300,
    108,
    58,
    31,
    17,
    9,
    5
];

var _lerp = function (a,  b,  c) {
    return a + c * (b - a);
};

var _altitudeToZoom = function(altitude, comparisonFunc) {
    var zoom = _altitudes.findIndex(function(zoomLevelDistance) {
        return comparisonFunc(altitude, zoomLevelDistance);
    });
    var maxZoom = _altitudes.length - 1;
    return (zoom === -1) ? maxZoom : zoom;
};

var _nearestZoomAbove = function(distance) {
    var zoomAbove = _altitudeToZoom(distance, function(d, z) { return d > z; });
    return Math.max(0, zoomAbove - 1);
};

var _nearestZoomBelow = function(distance) {
    return _altitudeToZoom(distance, function(d, z) { return d >= z; });
};

var zoomToDistance = function(zoom) {
    var zoomlevel = zoom;
    if(zoomlevel < 0) {
        zoomlevel =  0;
    }
    else if (zoomlevel >= _altitudes.length) {
        zoomlevel = _altitudes.length - 1;
    }

    var nearestZoomBelow = Math.floor(zoomlevel);
    var nearestZoomAbove = Math.ceil(zoomlevel);
    var valueBetweenNearestZoomLevels = zoomlevel - nearestZoomBelow;
    return _lerp(_altitudes[nearestZoomBelow], _altitudes[nearestZoomAbove], valueBetweenNearestZoomLevels);
};

var distanceToZoom = function(distance) {
    var smallestAltitude = _altitudes.length - 1;
    if(distance < _altitudes[smallestAltitude])
    {
        distance = _altitudes[smallestAltitude];
    }
    if(distance > _altitudes[0])
    {
        distance = _altitudes[0];
    }
    var nearestZoomAbove = _nearestZoomAbove(distance);
    var nearestZoomBelow = _nearestZoomBelow(distance);
    var distanceBetweenNearestZoomLevels = _altitudes[nearestZoomAbove] - _altitudes[nearestZoomBelow];
    var distanceFromZoomLevelBelow = distance - _altitudes[nearestZoomBelow];

    return (nearestZoomAbove === nearestZoomBelow) ? nearestZoomBelow : _lerp(nearestZoomBelow, nearestZoomAbove, distanceFromZoomLevelBelow / distanceBetweenNearestZoomLevels);
};

var space = {
    Vector3: Vector3,
    Vector4: Vector4,
    zoomToDistance: zoomToDistance,
    distanceToZoom: distanceToZoom
};

module.exports = space;<|endoftext|>


<commit_before>import {
  isFunction
} from './utils';

import {
  noop,
  nextId,
  PROMISE_ID,
  initializePromise
} from './-internal';

import {
  asap,
  setAsap,
  setScheduler
} from './asap';

import all from './promise/all';
import race from './promise/race';
import Resolve from './promise/resolve';
import Reject from './promise/reject';
import then from './then';

function needsResolver() {
  throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}

function needsNew() {
  throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}

/**
  Promise objects represent the eventual result of an asynchronous operation. The
  primary way of interacting with a promise is through its `then` method, which
  registers callbacks to receive either a promise's eventual value or the reason
  why the promise cannot be fulfilled.

  Terminology
  -----------

  - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
  - `thenable` is an object or function that defines a `then` method.
  - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
  - `exception` is a value that is thrown using the throw statement.
  - `reason` is a value that indicates why a promise was rejected.
  - `settled` the final resting state of a promise, fulfilled or rejected.

  A promise can be in one of three states: pending, fulfilled, or rejected.

  Promises that are fulfilled have a fulfillment value and are in the fulfilled
  state.  Promises that are rejected have a rejection reason and are in the
  rejected state.  A fulfillment value is never a thenable.

  Promises can also be said to *resolve* a value.  If this value is also a
  promise, then the original promise's settled state will match the value's
  settled state.  So a promise that *resolves* a promise that rejects will
  itself reject, and a promise that *resolves* a promise that fulfills will
  itself fulfill.

  Basic Usage:
  ------------

  ```js
  let promise = new Promise(function(resolve, reject) {
    // on success
    resolve(value);

    // on failure
    reject(reason);
  });

  promise.then(function(value) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });
  ```

  Advanced Usage:
  ---------------

  Promises shine when abstracting away asynchronous interactions such as
  `XMLHttpRequest`s.

  ```js
  function getJSON(url) {
    return new Promise(function(resolve, reject){
      let xhr = new XMLHttpRequest();

      xhr.open('GET', url);
      xhr.onreadystatechange = handler;
      xhr.responseType = 'json';
      xhr.setRequestHeader('Accept', 'application/json');
      xhr.send();

      function handler() {
        if (this.readyState === this.DONE) {
          if (this.status === 200) {
            resolve(this.response);
          } else {
            reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
          }
        }
      };
    });
  }

  getJSON('/posts.json').then(function(json) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });
  ```

  Unlike callbacks, promises are great composable primitives.

  ```js
  Promise.all([
    getJSON('/posts'),
    getJSON('/comments')
  ]).then(function(values){
    values[0] // => postsJSON
    values[1] // => commentsJSON

    return values;
  });
  ```

  @class Promise
  @param {function} resolver
  Useful for tooling.
  @constructor
*/
export default function Promise(resolver) {
  this[PROMISE_ID] = nextId();
  this._result = this._state = undefined;
  this._subscribers = [];

  if (noop !== resolver) {
    typeof resolver !== 'function' && needsResolver();
    this instanceof Promise ? initializePromise(this, resolver) : needsNew();
  }
}

Promise.all = all;
Promise.race = race;
Promise.resolve = Resolve;
Promise.reject = Reject;
Promise._setScheduler = setScheduler;
Promise._setAsap = setAsap;
Promise._asap = asap;

Promise.prototype = {
  constructor: Promise,

/**
  The primary way of interacting with a promise is through its `then` method,
  which registers callbacks to receive either a promise's eventual value or the
  reason why the promise cannot be fulfilled.

  ```js
  findUser().then(function(user){
    // user is available
  }, function(reason){
    // user is unavailable, and you are given the reason why
  });
  ```

  Chaining
  --------

  The return value of `then` is itself a promise.  This second, 'downstream'
  promise is resolved with the return value of the first promise's fulfillment
  or rejection handler, or rejected if the handler throws an exception.

  ```js
  findUser().then(function (user) {
    return user.name;
  }, function (reason) {
    return 'default name';
  }).then(function (userName) {
    // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
    // will be `'default name'`
  });

  findUser().then(function (user) {
    throw new Error('Found user, but still unhappy');
  }, function (reason) {
    throw new Error('`findUser` rejected and we're unhappy');
  }).then(function (value) {
    // never reached
  }, function (reason) {
    // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
    // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
  });
  ```
  If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.

  ```js
  findUser().then(function (user) {
    throw new PedagogicalException('Upstream error');
  }).then(function (value) {
    // never reached
  }).then(function (value) {
    // never reached
  }, function (reason) {
    // The `PedgagocialException` is propagated all the way down to here
  });
  ```

  Assimilation
  ------------

  Sometimes the value you want to propagate to a downstream promise can only be
  retrieved asynchronously. This can be achieved by returning a promise in the
  fulfillment or rejection handler. The downstream promise will then be pending
  until the returned promise is settled. This is called *assimilation*.

  ```js
  findUser().then(function (user) {
    return findCommentsByAuthor(user);
  }).then(function (comments) {
    // The user's comments are now available
  });
  ```

  If the assimliated promise rejects, then the downstream promise will also reject.

  ```js
  findUser().then(function (user) {
    return findCommentsByAuthor(user);
  }).then(function (comments) {
    // If `findCommentsByAuthor` fulfills, we'll have the value here
  }, function (reason) {
    // If `findCommentsByAuthor` rejects, we'll have the reason here
  });
  ```

  Simple Example
  --------------

  Synchronous Example

  ```javascript
  let result;

  try {
    result = findResult();
    // success
  } catch(reason) {
    // failure
  }
  ```

  Errback Example

  ```js
  findResult(function(result, err){
    if (err) {
      // failure
    } else {
      // success
    }
  });
  ```

  Promise Example;

  ```javascript
  findResult().then(function(result){
    // success
  }, function(reason){
    // failure
  });
  ```

  Advanced Example
  --------------

  Synchronous Example

  ```javascript
  let author, books;

  try {
    author = findAuthor();
    books  = findBooksByAuthor(author);
    // success
  } catch(reason) {
    // failure
  }
  ```

  Errback Example

  ```js

  function foundBooks(books) {

  }

  function failure(reason) {

  }

  findAuthor(function(author, err){
    if (err) {
      failure(err);
      // failure
    } else {
      try {
        findBoooksByAuthor(author, function(books, err) {
          if (err) {
            failure(err);
          } else {
            try {
              foundBooks(books);
            } catch(reason) {
              failure(reason);
            }
          }
        });
      } catch(error) {
        failure(err);
      }
      // success
    }
  });
  ```

  Promise Example;

  ```javascript
  findAuthor().
    then(findBooksByAuthor).
    then(function(books){
      // found books
  }).catch(function(reason){
    // something went wrong
  });
  ```

  @method then
  @param {Function} onFulfilled
  @param {Function} onRejected
  Useful for tooling.
  @return {Promise}
*/
  then: then,

/**
  `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
  as the catch block of a try/catch statement.

  ```js
  function findAuthor(){
    throw new Error('couldn't find that author');
  }

  // synchronous
  try {
    findAuthor();
  } catch(reason) {
    // something went wrong
  }

  // async with promises
  findAuthor().catch(function(reason){
    // something went wrong
  });
  ```

  @method catch
  @param {Function} onRejection
  Useful for tooling.
  @return {Promise}
*/
  catch(onRejection) {
    return this.then(null, onRejection);
  }
};
<commit_msg>Typo fix<commit_after>import {
  isFunction
} from './utils';

import {
  noop,
  nextId,
  PROMISE_ID,
  initializePromise
} from './-internal';

import {
  asap,
  setAsap,
  setScheduler
} from './asap';

import all from './promise/all';
import race from './promise/race';
import Resolve from './promise/resolve';
import Reject from './promise/reject';
import then from './then';

function needsResolver() {
  throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}

function needsNew() {
  throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}

/**
  Promise objects represent the eventual result of an asynchronous operation. The
  primary way of interacting with a promise is through its `then` method, which
  registers callbacks to receive either a promise's eventual value or the reason
  why the promise cannot be fulfilled.

  Terminology
  -----------

  - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
  - `thenable` is an object or function that defines a `then` method.
  - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
  - `exception` is a value that is thrown using the throw statement.
  - `reason` is a value that indicates why a promise was rejected.
  - `settled` the final resting state of a promise, fulfilled or rejected.

  A promise can be in one of three states: pending, fulfilled, or rejected.

  Promises that are fulfilled have a fulfillment value and are in the fulfilled
  state.  Promises that are rejected have a rejection reason and are in the
  rejected state.  A fulfillment value is never a thenable.

  Promises can also be said to *resolve* a value.  If this value is also a
  promise, then the original promise's settled state will match the value's
  settled state.  So a promise that *resolves* a promise that rejects will
  itself reject, and a promise that *resolves* a promise that fulfills will
  itself fulfill.

  Basic Usage:
  ------------

  ```js
  let promise = new Promise(function(resolve, reject) {
    // on success
    resolve(value);

    // on failure
    reject(reason);
  });

  promise.then(function(value) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });
  ```

  Advanced Usage:
  ---------------

  Promises shine when abstracting away asynchronous interactions such as
  `XMLHttpRequest`s.

  ```js
  function getJSON(url) {
    return new Promise(function(resolve, reject){
      let xhr = new XMLHttpRequest();

      xhr.open('GET', url);
      xhr.onreadystatechange = handler;
      xhr.responseType = 'json';
      xhr.setRequestHeader('Accept', 'application/json');
      xhr.send();

      function handler() {
        if (this.readyState === this.DONE) {
          if (this.status === 200) {
            resolve(this.response);
          } else {
            reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
          }
        }
      };
    });
  }

  getJSON('/posts.json').then(function(json) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });
  ```

  Unlike callbacks, promises are great composable primitives.

  ```js
  Promise.all([
    getJSON('/posts'),
    getJSON('/comments')
  ]).then(function(values){
    values[0] // => postsJSON
    values[1] // => commentsJSON

    return values;
  });
  ```

  @class Promise
  @param {Function} resolver
  Useful for tooling.
  @constructor
*/
export default function Promise(resolver) {
  this[PROMISE_ID] = nextId();
  this._result = this._state = undefined;
  this._subscribers = [];

  if (noop !== resolver) {
    typeof resolver !== 'function' && needsResolver();
    this instanceof Promise ? initializePromise(this, resolver) : needsNew();
  }
}

Promise.all = all;
Promise.race = race;
Promise.resolve = Resolve;
Promise.reject = Reject;
Promise._setScheduler = setScheduler;
Promise._setAsap = setAsap;
Promise._asap = asap;

Promise.prototype = {
  constructor: Promise,

/**
  The primary way of interacting with a promise is through its `then` method,
  which registers callbacks to receive either a promise's eventual value or the
  reason why the promise cannot be fulfilled.

  ```js
  findUser().then(function(user){
    // user is available
  }, function(reason){
    // user is unavailable, and you are given the reason why
  });
  ```

  Chaining
  --------

  The return value of `then` is itself a promise.  This second, 'downstream'
  promise is resolved with the return value of the first promise's fulfillment
  or rejection handler, or rejected if the handler throws an exception.

  ```js
  findUser().then(function (user) {
    return user.name;
  }, function (reason) {
    return 'default name';
  }).then(function (userName) {
    // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
    // will be `'default name'`
  });

  findUser().then(function (user) {
    throw new Error('Found user, but still unhappy');
  }, function (reason) {
    throw new Error('`findUser` rejected and we're unhappy');
  }).then(function (value) {
    // never reached
  }, function (reason) {
    // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
    // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
  });
  ```
  If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.

  ```js
  findUser().then(function (user) {
    throw new PedagogicalException('Upstream error');
  }).then(function (value) {
    // never reached
  }).then(function (value) {
    // never reached
  }, function (reason) {
    // The `PedgagocialException` is propagated all the way down to here
  });
  ```

  Assimilation
  ------------

  Sometimes the value you want to propagate to a downstream promise can only be
  retrieved asynchronously. This can be achieved by returning a promise in the
  fulfillment or rejection handler. The downstream promise will then be pending
  until the returned promise is settled. This is called *assimilation*.

  ```js
  findUser().then(function (user) {
    return findCommentsByAuthor(user);
  }).then(function (comments) {
    // The user's comments are now available
  });
  ```

  If the assimliated promise rejects, then the downstream promise will also reject.

  ```js
  findUser().then(function (user) {
    return findCommentsByAuthor(user);
  }).then(function (comments) {
    // If `findCommentsByAuthor` fulfills, we'll have the value here
  }, function (reason) {
    // If `findCommentsByAuthor` rejects, we'll have the reason here
  });
  ```

  Simple Example
  --------------

  Synchronous Example

  ```javascript
  let result;

  try {
    result = findResult();
    // success
  } catch(reason) {
    // failure
  }
  ```

  Errback Example

  ```js
  findResult(function(result, err){
    if (err) {
      // failure
    } else {
      // success
    }
  });
  ```

  Promise Example;

  ```javascript
  findResult().then(function(result){
    // success
  }, function(reason){
    // failure
  });
  ```

  Advanced Example
  --------------

  Synchronous Example

  ```javascript
  let author, books;

  try {
    author = findAuthor();
    books  = findBooksByAuthor(author);
    // success
  } catch(reason) {
    // failure
  }
  ```

  Errback Example

  ```js

  function foundBooks(books) {

  }

  function failure(reason) {

  }

  findAuthor(function(author, err){
    if (err) {
      failure(err);
      // failure
    } else {
      try {
        findBoooksByAuthor(author, function(books, err) {
          if (err) {
            failure(err);
          } else {
            try {
              foundBooks(books);
            } catch(reason) {
              failure(reason);
            }
          }
        });
      } catch(error) {
        failure(err);
      }
      // success
    }
  });
  ```

  Promise Example;

  ```javascript
  findAuthor().
    then(findBooksByAuthor).
    then(function(books){
      // found books
  }).catch(function(reason){
    // something went wrong
  });
  ```

  @method then
  @param {Function} onFulfilled
  @param {Function} onRejected
  Useful for tooling.
  @return {Promise}
*/
  then: then,

/**
  `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
  as the catch block of a try/catch statement.

  ```js
  function findAuthor(){
    throw new Error('couldn't find that author');
  }

  // synchronous
  try {
    findAuthor();
  } catch(reason) {
    // something went wrong
  }

  // async with promises
  findAuthor().catch(function(reason){
    // something went wrong
  });
  ```

  @method catch
  @param {Function} onRejection
  Useful for tooling.
  @return {Promise}
*/
  catch(onRejection) {
    return this.then(null, onRejection);
  }
};
<|endoftext|>


<commit_before>module.exports = function(config) {
  'use strict';
  return {
    options: {
      livereload: false
    },

    assets: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/{fonts,images}/**/*',
        '<%= pkg.gruntConfig.clientDir %>/index.html',
        '<%= pkg.gruntConfig.clientDir %>/favicon.ico'
      ],
      tasks: [
        'newer:copy:assets'
      ]
    },

    styles: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/styles/**/*.{css,less}',
        '<%= pkg.gruntConfig.clientDir %>/scripts/**/*.{css,less}'
      ],
      tasks: [
        'less'
      ]
    },

    scripts: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/scripts/**/*.{js,html,json}'
      ],
      tasks: [
        'newer:jshint:scripts',
        'localescompile',
        'requirejs:scripts'
      ]
    },

    config: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/scripts/config/config.js'
      ],
      tasks: [
        'copy:config'
      ]
    },

    sdk: {
      files: [
        'node_modules/camunda-commons-ui/lib/**/*.js',
        'node_modules/camunda-commons-ui/{resources,lib/*}/locales/**/*.json',
        'node_modules/camunda-bpm-sdk-js/dist/**/*.js'
      ],
      tasks: [
        'localescompile',
        'requirejs:scripts',
        'requirejs:dependencies'
      ]
    },

    served: {
      files: ['<%= buildTarget %>/**/*'],
      options: {
        livereload: config.livereloadPort || false
      }
    }
  };
};
<commit_msg>impr(watch): align served watch task<commit_after>module.exports = function(config) {
  'use strict';
  return {
    options: {
      livereload: false
    },

    assets: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/{fonts,images}/**/*',
        '<%= pkg.gruntConfig.clientDir %>/index.html',
        '<%= pkg.gruntConfig.clientDir %>/favicon.ico'
      ],
      tasks: [
        'newer:copy:assets'
      ]
    },

    styles: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/styles/**/*.{css,less}',
        '<%= pkg.gruntConfig.clientDir %>/scripts/**/*.{css,less}'
      ],
      tasks: [
        'less'
      ]
    },

    scripts: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/scripts/**/*.{js,html,json}'
      ],
      tasks: [
        'newer:jshint:scripts',
        'localescompile',
        'requirejs:scripts'
      ]
    },

    config: {
      files: [
        '<%= pkg.gruntConfig.clientDir %>/scripts/config/config.js'
      ],
      tasks: [
        'copy:config'
      ]
    },

    sdk: {
      files: [
        'node_modules/camunda-commons-ui/lib/**/*.js',
        'node_modules/camunda-commons-ui/{resources,lib/*}/locales/**/*.json',
        'node_modules/camunda-bpm-sdk-js/dist/**/*.js'
      ],
      tasks: [
        'localescompile',
        'requirejs:scripts',
        'requirejs:dependencies'
      ]
    },

    served: {
      options: {
        cwd: '<%= buildTarget %>/',
        livereload: config.livereloadPort || false
      },
      files: '**/*.{css,html,js}'
    }
  };
};
<|endoftext|>