<commit_before>var C2 = 65.41; // C2 note, in Hz.
var notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
var octaves = ["2", "3", "4", "5", "6", "7"];
var test_frequencies = [];
var note_map =
	["222E2L","214F2","206G2","198A2","190B2","182C3","174D3","166E3","158F3","150G3","143A3","135B3","127C4L"
	,"111C4L","103D4","095E4","087F4","080G4","072A4","064B4","056C5","048D5","040E5","032F5","024G5","016A5L"];
// The above encodes note info like so: first 3 digits represent y coord, letter represents note name
// next digit represents octave, L is added at the end if a ledger line is needed.
var current_note = "";
var max_whitenoise = 0;
var whitenoise_measurements = 0;
var notes_played = 0;
var staff_notes = ["", "", "", "", "", "", "", ""];
var min_note = 0;
var max_note = 26;
var bar = true; // bar in the code refers to the bar moving across the screen dictating when to play notes.
var notes_passed = 0;
var bar_duration = 30000;
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audio_context = null;
var ios = false;
for (var i = 0; i < 72; i++) { // Fill up the mapping between frequencies and notes
	var note_frequency = C2 * Math.pow(2, i / 12);
	var note_name = notes[i % 12] + octaves[Math.floor(i / 12)];
	var note = { "frequency": note_frequency, "name": note_name };
	test_frequencies = test_frequencies.concat([note]);
}

window.addEventListener("load", initialize);
var correlation_worker = new Worker("correlation_worker.js");
correlation_worker.addEventListener("message", interpret_correlation_result);
document.getElementById("minnote").addEventListener("change", update_note_range);
document.getElementById("maxnote").addEventListener("change", update_note_range);
document.getElementById("resume").addEventListener("click", function iosfixer() {
	audio_context.resume(); // audio_context starts paused on iOS
	ios = true; // in case I need to design around iOS in the future
});
document.getElementById("barcheckbox").addEventListener("click", function toggle_bpm_field() {
	if ($("#barcheckbox").prop("checked")) { $("[id^='bpm']").fadeIn(0); }
	else { $("[id^='bpm']").fadeOut(0); }
});

function initialize() {
	var get_user_media = navigator.getUserMedia;
	get_user_media = get_user_media || navigator.webkitGetUserMedia;
	get_user_media = get_user_media || navigator.mozGetUserMedia;
	get_user_media.call(navigator, { "audio": true }, use_stream, function () { $("#loading").text("Error: Microphone Unavailable"); });
	if (!$("#barcheckbox").prop("checked")) { $("[id^='bpm']").fadeOut(0); }
	update_note_range(); //accounts for autocomplete
	use_bar();
}

function use_stream(stream) {
	audio_context = new AudioContext();
	var microphone = audio_context.createMediaStreamSource(stream);
	var script_processor = audio_context.createScriptProcessor(1024, 1, 1);
	script_processor.connect(audio_context.destination);
	microphone.connect(script_processor); // four lines set up microphone
	var buffer = [];
	var sample_length_milliseconds = 50;
	var recording = true;
	script_processor.onaudioprocess = function (event) {
		if (!recording) return;
		buffer = buffer.concat(Array.prototype.slice.call(event.inputBuffer.getChannelData(0)));
		// Stop recording after sample_length_milliseconds.
		if (buffer.length > sample_length_milliseconds * audio_context.sampleRate / 1000) {
			recording = false;
			correlation_worker.postMessage(
				{
					"timeseries": buffer,
					"test_frequencies": test_frequencies,
					"sample_rate": audio_context.sampleRate
				});
			buffer = [];
			setTimeout(function () { recording = true; }, 250);
		}
	};
}

function interpret_correlation_result(event) {
	var timeseries = event.data.timeseries;
	var frequency_amplitudes = event.data.frequency_amplitudes;
	// Compute the (squared) magnitudes of the complex amplitudes for each
	// test frequency.
	var magnitudes = frequency_amplitudes.map(function (z) {
		return z[0] * z[0] + z[1] * z[1];
	});
	// Find the maximum in the list of magnitudes.
	var maximum_index = -1;
	var maximum_magnitude = 0;
	for (var i = 0; i < magnitudes.length; i++) {
		if (magnitudes[i] <= maximum_magnitude) continue;
		maximum_index = i;
		maximum_magnitude = magnitudes[i];
	}
	if (whitenoise_measurements < 5) { // The white noisemeasurements make sure that white noise doesn't register as a note.
		$("#loading").text("Calibrating microphone:" + (whitenoise_measurements + 1) * 100 / 5 + "%");
		whitenoise_measurements++;
		if (max_whitenoise < maximum_magnitude) { max_whitenoise = maximum_magnitude; }
	}
	if (whitenoise_measurements === 5) { // Once enough data on whitenoise is gathered, generate sheet music and start listening.
		$("#loading").text("");
		whitenoise_measurements++;
		start_practice();
	}
	// Compute the average magnitude. We'll only pay attention to frequencies
	// with magnitudes significantly above average.
	var average = magnitudes.reduce(function (a, b) { return a + b; }, 0) / magnitudes.length;
	var confidence = maximum_magnitude / average;
	var confidence_threshold = 15; // empirical, arbitrary.
	if (confidence > confidence_threshold && maximum_magnitude > max_whitenoise * 3) {
		var dominant_frequency = test_frequencies[maximum_index];
		var a = test_frequencies[maximum_index + 12]; //The algorithm can be off by 1 octave, so need these as workarounds.
		var b = test_frequencies[maximum_index - 12];
		console.log("expected" + current_note + "actual" + dominant_frequency.name);
		if (dominant_frequency.name === current_note || a.name === current_note || b.name === current_note) { continue_practice(true); }
	}
}

function start_practice() {
	bar = $("#barcheckbox").prop("checked");
	bar_duration = 540000 / $("#bpm").val(); // Conversion of user input to animation speed.
	notes_passed = 0;
	notes_played = 0;
	$("#bar").css("left", "100px");
	if (bar) { $("#bar").animate({ left: "550px" }, bar_duration, "linear", start_practice); }
	$("[id^='note']").fadeIn(0);
	$("[id^='sharp']").fadeOut(0);
	$("[id^='note']").each(function (note_num) {
		var note_info = note_map[Math.floor(+min_note + Math.random() * (max_note - min_note))]; // Decide which note to generate.
		var temp_note = note_info.substring(3, 5);
		if (note_info.substring(3, 4).match("[CDFGA]") && Math.random() > 0.50) { // Make notes sharp.
			temp_note = note_info.substring(3, 4) + "#" + note_info.substring(4, 5);
			$("#sharp" + note_num).fadeIn(0);
		}
		staff_notes[note_num] = temp_note;
		this.style.top = parseInt(note_info.substring(0, 3), 10) + "px";
		$("#sharp" + note_num).css("top", parseInt(note_info.substring(0, 3)) - 12 + "px");
		this.src = note_info.length === 6 ? "notewithline.png" : "note.png"; //length is only 6 when there is an L in note_info
	});
	current_note = staff_notes[0];
}

function continue_practice(success) { //success = true when note is played, false when bar passes over note without being played
	if (notes_played === 7 && !bar) { start_practice(); }
	else {
		if (success && (!bar || notes_played < notes_passed + 1)) { $("[id $=" + notes_played + "]").fadeOut(500); }
		else if (success) { return; } // Occurs when user plays faster than bar.
		else { $("[id $=note" + notes_played + "]").prop("src", "rednote.png"); }
		notes_played++; // Please note the order of these statements if you're going through the code in your head.
		current_note = staff_notes[notes_played];
	}
}

async function use_bar() {
	while (true) {
		await new Promise(resolve => setTimeout(resolve, 10));
		if (parseInt($("#bar").css("left").substring(0, 3)) > 170 + notes_passed * 50) {
			notes_passed++;
			if (notes_passed > notes_played) { continue_practice(false); }
		}
	}
}

async function update_note_range() {
	await new Promise(resolve => setTimeout(resolve, 5)); // Display glitches pop up if I don't wait a few milliseconds.
	min_note = $("#minnote").val();
	max_note = $("#maxnote").val();
	if (+min_note > +max_note) { $("#maxnote").val(+min_note + 1); update_note_range(); }
	$("#minnotedisplay").text("Lowest note: " + note_map[min_note].substring(3, 5));
	$("#maxnotedisplay").text("Highest note: E2"); // displayed if max_note === 0
	$("#maxnotedisplay").text("Highest note: " + note_map[max_note - 1].substring(3, 5));
}<commit_msg>minor bugfix<commit_after>var C2 = 65.41; // C2 note, in Hz.
var notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
var octaves = ["2", "3", "4", "5", "6", "7"];
var test_frequencies = [];
var note_map =
	["222E2L","214F2","206G2","198A2","190B2","182C3","174D3","166E3","158F3","150G3","143A3","135B3","127C4L"
	,"111C4L","103D4","095E4","087F4","080G4","072A4","064B4","056C5","048D5","040E5","032F5","024G5","016A5L"];
// The above encodes note info like so: first 3 digits represent y coord, letter represents note name
// next digit represents octave, L is added at the end if a ledger line is needed.
var current_note = "";
var max_whitenoise = 0;
var whitenoise_measurements = 0;
var notes_played = 0;
var staff_notes = ["", "", "", "", "", "", "", ""];
var min_note = 0;
var max_note = 26;
var bar = true; // bar in the code refers to the bar moving across the screen dictating when to play notes.
var notes_passed = 0;
var bar_duration = 30000;
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audio_context = null;
var ios = false;
for (var i = 0; i < 72; i++) { // Fill up the mapping between frequencies and notes
	var note_frequency = C2 * Math.pow(2, i / 12);
	var note_name = notes[i % 12] + octaves[Math.floor(i / 12)];
	var note = { "frequency": note_frequency, "name": note_name };
	test_frequencies = test_frequencies.concat([note]);
}

window.addEventListener("load", initialize);
var correlation_worker = new Worker("correlation_worker.js");
correlation_worker.addEventListener("message", interpret_correlation_result);
document.getElementById("minnote").addEventListener("change", update_note_range);
document.getElementById("maxnote").addEventListener("change", update_note_range);
document.getElementById("resume").addEventListener("click", function iosfixer() {
	audio_context.resume(); // audio_context starts paused on iOS
	ios = true; // in case I need to design around iOS in the future
});
document.getElementById("barcheckbox").addEventListener("click", function toggle_bpm_field() {
	if ($("#barcheckbox").prop("checked")) { $("[id^='bpm']").fadeIn(0); }
	else { $("[id^='bpm']").fadeOut(0); }
});

function initialize() {
	var get_user_media = navigator.getUserMedia;
	get_user_media = get_user_media || navigator.webkitGetUserMedia;
	get_user_media = get_user_media || navigator.mozGetUserMedia;
	get_user_media.call(navigator, { "audio": true }, use_stream, function () { $("#loading").text("Error: Microphone Unavailable"); });
	if (!$("#barcheckbox").prop("checked")) { $("[id^='bpm']").fadeOut(0); }
	update_note_range(); //accounts for autocomplete
	use_bar();
}

function use_stream(stream) {
	audio_context = new AudioContext();
	var microphone = audio_context.createMediaStreamSource(stream);
	var script_processor = audio_context.createScriptProcessor(1024, 1, 1);
	script_processor.connect(audio_context.destination);
	microphone.connect(script_processor); // four lines set up microphone
	var buffer = [];
	var sample_length_milliseconds = 50;
	var recording = true;
	// Need this in global namespace so it doesn't get garbage-collected
	window.process_audio = function (event) {
		if (!recording) return;
		buffer = buffer.concat(Array.prototype.slice.call(event.inputBuffer.getChannelData(0)));
		// Stop recording after sample_length_milliseconds.
		if (buffer.length > sample_length_milliseconds * audio_context.sampleRate / 1000) {
			recording = false;
			correlation_worker.postMessage(
				{
					"timeseries": buffer,
					"test_frequencies": test_frequencies,
					"sample_rate": audio_context.sampleRate
				});
			buffer = [];
			setTimeout(function () { recording = true; }, 250);
		}
	};
	script_processor.onaudioprocess = window.process_audio;
}

function interpret_correlation_result(event) {
	var timeseries = event.data.timeseries;
	var frequency_amplitudes = event.data.frequency_amplitudes;
	// Compute the (squared) magnitudes of the complex amplitudes for each
	// test frequency.
	var magnitudes = frequency_amplitudes.map(function (z) {
		return z[0] * z[0] + z[1] * z[1];
	});
	// Find the maximum in the list of magnitudes.
	var maximum_index = -1;
	var maximum_magnitude = 0;
	for (var i = 0; i < magnitudes.length; i++) {
		if (magnitudes[i] <= maximum_magnitude) continue;
		maximum_index = i;
		maximum_magnitude = magnitudes[i];
	}
	if (whitenoise_measurements < 5) { // The white noisemeasurements make sure that white noise doesn't register as a note.
		$("#loading").text("Calibrating microphone:" + (whitenoise_measurements + 1) * 100 / 5 + "%");
		whitenoise_measurements++;
		if (max_whitenoise < maximum_magnitude) { max_whitenoise = maximum_magnitude; }
	}
	if (whitenoise_measurements === 5) { // Once enough data on whitenoise is gathered, generate sheet music and start listening.
		$("#loading").text("");
		whitenoise_measurements++;
		start_practice();
	}
	// Compute the average magnitude. We'll only pay attention to frequencies
	// with magnitudes significantly above average.
	var average = magnitudes.reduce(function (a, b) { return a + b; }, 0) / magnitudes.length;
	var confidence = maximum_magnitude / average;
	var confidence_threshold = 15; // empirical, arbitrary.
	if (confidence > confidence_threshold && maximum_magnitude > max_whitenoise * 3) {
		var dominant_frequency = test_frequencies[maximum_index];
		var a = test_frequencies[maximum_index + 12]; //The algorithm can be off by 1 octave, so need these as workarounds.
		var b = test_frequencies[maximum_index - 12];
		console.log("expected" + current_note + "actual" + dominant_frequency.name);
		if (dominant_frequency.name === current_note || a.name === current_note || b.name === current_note) { continue_practice(true); }
	}
}

function start_practice() {
	bar = $("#barcheckbox").prop("checked");
	bar_duration = 540000 / $("#bpm").val(); // Conversion of user input to animation speed.
	notes_passed = 0;
	notes_played = 0;
	$("#bar").css("left", "100px");
	if (bar) { $("#bar").animate({ left: "550px" }, bar_duration, "linear", start_practice); }
	$("[id^='note']").fadeIn(0);
	$("[id^='sharp']").fadeOut(0);
	$("[id^='note']").each(function (note_num) {
		var note_info = note_map[Math.floor(+min_note + Math.random() * (max_note - min_note))]; // Decide which note to generate.
		var temp_note = note_info.substring(3, 5);
		if (note_info.substring(3, 4).match("[CDFGA]") && Math.random() > 0.50) { // Make notes sharp.
			temp_note = note_info.substring(3, 4) + "#" + note_info.substring(4, 5);
			$("#sharp" + note_num).fadeIn(0);
		}
		staff_notes[note_num] = temp_note;
		this.style.top = parseInt(note_info.substring(0, 3), 10) + "px";
		$("#sharp" + note_num).css("top", parseInt(note_info.substring(0, 3)) - 12 + "px");
		this.src = note_info.length === 6 ? "notewithline.png" : "note.png"; //length is only 6 when there is an L in note_info
	});
	current_note = staff_notes[0];
}

function continue_practice(success) { //success = true when note is played, false when bar passes over note without being played
	if (notes_played === 7 && !bar) { start_practice(); }
	else {
		if (success && (!bar || notes_played < notes_passed + 1)) { $("[id $=" + notes_played + "]").fadeOut(500); }
		else if (success) { return; } // Occurs when user plays faster than bar.
		else { $("[id $=note" + notes_played + "]").prop("src", "rednote.png"); }
		notes_played++; // Please note the order of these statements if you're going through the code in your head.
		current_note = staff_notes[notes_played];
	}
}

async function use_bar() {
	while (true) {
		await new Promise(resolve => setTimeout(resolve, 10));
		if (parseInt($("#bar").css("left").substring(0, 3)) > 170 + notes_passed * 50) {
			notes_passed++;
			if (notes_passed > notes_played) { continue_practice(false); }
		}
	}
}

async function update_note_range() {
	await new Promise(resolve => setTimeout(resolve, 5)); // Display glitches pop up if I don't wait a few milliseconds.
	min_note = $("#minnote").val();
	max_note = $("#maxnote").val();
	if (+min_note > +max_note) { $("#maxnote").val(+min_note + 1); update_note_range(); }
	$("#minnotedisplay").text("Lowest note: " + note_map[min_note].substring(3, 5));
	$("#maxnotedisplay").text("Highest note: E2"); // displayed if max_note === 0
	$("#maxnotedisplay").text("Highest note: " + note_map[max_note - 1].substring(3, 5));
}<|endoftext|>


<commit_before>/**
 * Created by FeiyuLab on 2014/11/26 0026.
 */
"use strict";
(function(window,undefined){
    /**
     * 嵌套IF，判断是否有AMD规范
     */
    (function(factory){
        if ( typeof define === 'function' && define.amd ) {
            // Define as an AMD module if possible
            define( factory );
        }else  {
            factory( window ,true);
        }
        /**
         * noGlobal用于判断是否注册到Window
         */
    }(function(window,noGlobal){
        var fsmClass={ASYNC:"FLAG_ASYNC"},
            cache={},//用$id管理实例的私有属性
            debug = true,
            FSMclasses={},//所有FSM类
            FSMinitSettings={}//FSM类对应的initial属性
        /******************************FSM基类*****************************************************/
        function BaseFSM(){
            this.current="none"
        }
        BaseFSM.prototype={
            constructor:BaseFSM,
            transition:function(){
                var tmpcache=cache[this.$id]
                if(tmpcache.isAsync){
                    log("Start Async Transiton")
                    tmpcache.beginStateChange()
                }
            },
            cancel:function(){
                var tmpcache=cache[this.$id]
                if(tmpcache.isAsync){
                    log("Cancel Async Waiting")
                    tmpcache.isAsync=false
                    tmpcache.beginStateChange=null;
                }
            },
            is:function(state){
                return this.current === state
            },
            can:function(eventName){
                return getToByFrom(this[eventName].path,this.current) !== undefined
            },
            cannot:function(eventName){
                return !this.can(eventName)
            }
        }
        /****************************************************************************************/
        /**
         * 由settingObj生成继承FSM基类的用户自定义FSM类
         * @param settingObj
         * @returns {UserFSM}
         */
        function createUserFSM(settingObj){
            var UserFSM=function(stateInit){
                BaseFSM.call(this)
                stateInit && (this.current = stateInit)
            }
            inheritFromSuperType(UserFSM,BaseFSM)
            each(settingObj.callbacks,function(name,callback){
                UserFSM.prototype[name]=callback
            })
            each(settingObj.events,function(i,event){
                UserFSM.prototype[event.name]=UserFSM.prototype[event.name]||buildEventFunction(UserFSM,event)
                UserFSM.prototype[event.name].path= UserFSM.prototype[event.name].path||[]
                UserFSM.prototype[event.name].path.push({from:event.from,to:event.to})
            })
            UserFSM.error = settingObj.error || printError
            return UserFSM;
        }

        /**
         * 遍历路径，找到from对应的to
         * @param pathInfo
         * @param from
         * @returns {*}
         */
        function getToByFrom(pathInfo,from){
            var finalTo
            each(pathInfo,function(i,path){
                if(typeof path.to == "string" && path.to !== "*"){
                    if(path.from instanceof Array){
                        finalTo = path.from.indexOf(from) != -1 ?path.to:finalTo
                        if(finalTo) return false;
                    }else if(path.from == "*" || path.from === from){
                        finalTo = path.to
                        return false;
                    }
                }
            })
            return finalTo
        }

        /**
         * 用户自定义的状态变迁事件都会调用此函数，如果调用非法则抛出错误
         * @param FSM
         * @param event
         * @returns {Function}
         */
        function buildEventFunction(FSM,event){
            return function(otherArguments){
                try{
                    if(cache[this.$id].isAsync)
                        throw "DisAllow this Method When Async"
                    else if(this.cannot(event.name))
                        throw "DisAllow this Method In Current State "+this.current
                    else
                        eventExecuter(this,FSM, event,otherArguments)
                }catch (e){
                    FSM.error(event.name,null,null,null,null,e)
                }

            }
        }

        /**
         * 状态变迁事件的核心函数，依次触发所有callback，并处理异步和取消事件的特殊情况
         * @param FSMInstance
         * @param FSMClass
         * @param event
         * @param finalTo
         * @param otherArguments
         */
        function eventExecuter(FSMInstance,FSMClass,event,finalTo,otherArguments){
            var name=event.name,path=FSMClass.path,from=FSMInstance.current,to=finalTo,prototype=FSMClass.prototype,
                canContinueEvent=true,useAsync=false,tmpFlag,tmpcache=cache[FSMInstance.$id],
                onbeforeE=prototype["onbefore"+name],
                onafterE=prototype["on"+name] || prototype["onafter"+name],
                onleaveS=prototype["onleave"+from],
                onenterS=prototype["onenter"+to] || prototype["on"+to],
                onbeforeEAll=prototype["onbeforeevent"],
                onafterEAll=prototype["onevent"] || prototype["onafterevent"],
                onleaveSAll=prototype["onleaveevent"],
                onenterSAll=prototype["onstate"] || prototype["onenterstate"],
                realArguments=[name, from,to].concat(otherArguments)
            if(isFunction(onbeforeE)){
                tmpFlag=onbeforeE(realArguments)
                tmpFlag===false && (canContinueEvent=false)
                tmpFlag===fsmClass.ASYNC && (useAsync=useAsync||true)
                log("execute onBefore "+name);
            }
            if(isFunction(onbeforeEAll)){
                onbeforeEAll(realArguments)
                log("execute onBeforeEvent");
            }
            if(isFunction(onleaveS)){
                tmpFlag=onleaveS(realArguments)
                tmpFlag===false && (canContinueEvent=false)
                tmpFlag===fsmClass.ASYNC && (useAsync=useAsync||true)
                log("execute onLeave "+from);
            }
            if(isFunction(onleaveSAll)){
                onleaveSAll(realArguments)
                log("execute onLeaveState");
            }
            if(useAsync===false)
                beginStateChange()
            else{
                tmpcache.isAsync=true
                tmpcache.beginStateChange=beginStateChange
                log("Async : waiting for Manual Transition()");
            }
            function beginStateChange(){
                if(canContinueEvent){
                    FSMInstance.current = to
                    if(isFunction(onenterS)){
                        onenterS(realArguments)
                        log("execute onEnterState "+to);
                    }
                    if(isFunction(onenterSAll)){
                        onenterSAll(realArguments)
                        log("execute onEnterState");
                    }
                    if(isFunction(onafterE)){
                        onafterE(realArguments)
                        log("execute onAfter "+name);
                    }
                    if(isFunction(onafterEAll)){
                        onafterEAll(realArguments)
                        log("execute onAfterEvent");
                    }
                }
                useAsync=false
                tmpcache.isAsync=false
                tmpcache.beginStateChange=null
            }
        }

        /**
         * 默认的Exception处理函数
         * @param eventName
         * @param from
         * @param to
         * @param args
         * @param errorCode
         * @param errorMessage
         */
        function printError(eventName, from, to, args, errorCode, errorMessage){
            log('Exception catched : when execute event ' + eventName + ' : '
                + errorMessage)
        }

        /**
         * 创建FSM，有4种重载形式
         *  create(settingObj)
         *  create(settingObj,ClassName)
         *  create(settingObj,ClassName,onlyClass)
         *  create(ClassName)
         * @returns {null}
         */
        fsmClass.create = function () {
            var createFromExistedFSMClass,settingObj,FSMName,hasParamErr=true,createClassOnly=false
            if(arguments.length==3 && typeof arguments[0] === "object"
                && typeof arguments[1] === "string" && typeof arguments[2] === "boolean"){
                createFromExistedFSMClass=false
                settingObj=arguments[0]
                FSMName=arguments[1]
                createClassOnly=arguments[2]
                hasParamErr=false
            }
            if(arguments.length==2 && typeof arguments[0] === "object" && typeof arguments[1] === "string"){
                createFromExistedFSMClass=false
                settingObj=arguments[0]
                FSMName=arguments[1]
                hasParamErr=false
            }else if(arguments.length === 1){
                switch (typeof arguments[0]){
                    case "object":
                        createFromExistedFSMClass=false;
                        settingObj=arguments[0]
                        hasParamErr=false
                        break;
                    case "string":
                        if(FSMclasses[arguments[0]] !== undefined){
                            createFromExistedFSMClass=true
                            FSMName=arguments[0]
                            settingObj={initial:FSMinitSettings[FSMName]}
                            hasParamErr=false
                        }
                        break;
                }
            }
            if(hasParamErr) {log("FSM.create : error in params") ; return null}
            var userfsmConstructor = createFromExistedFSMClass?FSMclasses[FSMName]:createUserFSM(settingObj),
                complexInitial = typeof settingObj.initial === "object",
                initialState = complexInitial?settingObj.initial.state:settingObj.initial,
                startupEvent = complexInitial?userfsmConstructor.prototype[settingObj.initial.event]:undefined,
                userfsmInstance = createClassOnly?null:new userfsmConstructor(initialState),
                $id = (Math.random()+'').substr(2)
            if(FSMName){
                FSMclasses[FSMName]=userfsmConstructor
                FSMinitSettings[FSMName]=settingObj.initial
            }
            if(createClassOnly===false){
                userfsmInstance.$id = $id
                cache[$id] = {isAsync:false,beginStateChange:null}
                startupEvent && startupEvent.call(userfsmInstance)
                return userfsmInstance;
            }
        }
        fsmClass.FSMs=FSMclasses

        if ( typeof noGlobal !== typeof undefined) {//若noGlobal为undefined，不注册到window
            window.FSM = fsmClass;
        }
        return fsmClass
        /**
         * 寄生组合式继承
         * @param subType
         * @param superType
         */
        function inheritFromSuperType(subType,superType){
            var UserFSM=function(){};//临时构造函数，收集共用方法
            if(typeof subType ==="function" && typeof superType === "function"){
                UserFSM.prototype=superType.prototype
                var prototype=new UserFSM();
                prototype.constructor=subType//修复constructor指向
                subType.prototype=prototype;
            }
        }
        function noop(){}
        function each(array,func){
            if(typeof func === "function" && typeof array === "object"){
                if(array instanceof Array){
                    for(var i= 0,length=array.length, one=array[0];i<length;i++,one = array[i]){
                        if(func(i, one) === false)
                            break;
                    }
                }else{
                    for(var j in array){
                        if(func(j, array[j])===false)
                            break;
                    }
                }
            }
        }
        function isFunction(func){
            return typeof func === "function"
        }
        function log(content){
            debug && console.log(content)
        }
    }))
})(window)
<commit_msg>fix bugs on eventFunction<commit_after>/**
 * Created by FeiyuLab on 2014/11/26 0026.
 */
"use strict";
(function(window,undefined){
    /**
     * 嵌套IF，判断是否有AMD规范
     */
    (function(factory){
        if ( typeof define === 'function' && define.amd ) {
            // Define as an AMD module if possible
            define( factory );
        }else  {
            factory( window ,true);
        }
        /**
         * noGlobal用于判断是否注册到Window
         */
    }(function(window,noGlobal){
        var fsmClass={ASYNC:"FLAG_ASYNC"},
            cache={},//用$id管理实例的私有属性
            debug = true,
            FSMclasses={},//所有FSM类
            FSMinitSettings={}//FSM类对应的initial属性
        /******************************FSM基类*****************************************************/
        function BaseFSM(){
            this.current="none"
        }
        BaseFSM.prototype={
            constructor:BaseFSM,
            transition:function(){
                var tmpcache=cache[this.$id]
                if(tmpcache.isAsync){
                    log("Start Async Transiton")
                    tmpcache.beginStateChange()
                }
            },
            cancel:function(){
                var tmpcache=cache[this.$id]
                if(tmpcache.isAsync){
                    log("Cancel Async Waiting")
                    tmpcache.isAsync=false
                    tmpcache.beginStateChange=null;
                }
            },
            is:function(state){
                return this.current === state
            },
            can:function(eventName){
                return getToByFrom(this[eventName].path,this.current) !== undefined
            },
            cannot:function(eventName){
                return !this.can(eventName)
            }
        }
        /****************************************************************************************/
        /**
         * 由settingObj生成继承FSM基类的用户自定义FSM类
         * @param settingObj
         * @returns {UserFSM}
         */
        function createUserFSM(settingObj){
            var UserFSM=function(stateInit){
                BaseFSM.call(this)
                stateInit && (this.current = stateInit)
            }
            inheritFromSuperType(UserFSM,BaseFSM)
            each(settingObj.callbacks,function(name,callback){
                UserFSM.prototype[name]=callback
            })
            each(settingObj.events,function(i,event){
                UserFSM.prototype[event.name]=UserFSM.prototype[event.name]||buildEventFunction(UserFSM,event)
                UserFSM.prototype[event.name].path= UserFSM.prototype[event.name].path||[]
                UserFSM.prototype[event.name].path.push({from:event.from,to:event.to})
            })
            UserFSM.error = settingObj.error || printError
            return UserFSM;
        }

        /**
         * 遍历路径，找到from对应的to
         * @param pathInfo
         * @param from
         * @returns {*}
         */
        function getToByFrom(pathInfo,from){
            var finalTo
            each(pathInfo,function(i,path){
                if(typeof path.to == "string" && path.to !== "*"){
                    if(path.from instanceof Array){
                        finalTo = path.from.indexOf(from) != -1 ?path.to:finalTo
                        if(finalTo) return false;
                    }else if(path.from == "*" || path.from === from){
                        finalTo = path.to
                        return false;
                    }
                }
            })
            return finalTo
        }

        /**
         * 用户自定义的状态变迁事件都会调用此函数，如果调用非法则抛出错误
         * @param FSM
         * @param event
         * @returns {Function}
         */
        function buildEventFunction(FSM,event){
            return function(otherArguments){
                try{
                    if(cache[this.$id].isAsync)
                        throw "DisAllow this Method When Async"
                    else if(this.cannot(event.name))
                        throw "DisAllow this Method In Current State "+this.current
                    else
                        eventExecuter(this,FSM, event,event.to,otherArguments)
                }catch (e){
                    FSM.error(event.name,null,null,null,null,e)
                }

            }
        }

        /**
         * 状态变迁事件的核心函数，依次触发所有callback，并处理异步和取消事件的特殊情况
         * @param FSMInstance
         * @param FSMClass
         * @param event
         * @param finalTo
         * @param otherArguments
         */
        function eventExecuter(FSMInstance,FSMClass,event,finalTo,otherArguments){
            var name=event.name,path=FSMClass.path,from=FSMInstance.current,to=finalTo,prototype=FSMClass.prototype,
                canContinueEvent=true,useAsync=false,tmpFlag,tmpcache=cache[FSMInstance.$id],
                onbeforeE=prototype["onbefore"+name],
                onafterE=prototype["on"+name] || prototype["onafter"+name],
                onleaveS=prototype["onleave"+from],
                onenterS=prototype["onenter"+to] || prototype["on"+to],
                onbeforeEAll=prototype["onbeforeevent"],
                onafterEAll=prototype["onevent"] || prototype["onafterevent"],
                onleaveSAll=prototype["onleaveevent"],
                onenterSAll=prototype["onstate"] || prototype["onenterstate"],
                realArguments=[name, from,to].concat(otherArguments)
            if(isFunction(onbeforeE)){
                tmpFlag=onbeforeE.apply(onbeforeE,realArguments)
                tmpFlag===false && (canContinueEvent=false)
                tmpFlag===fsmClass.ASYNC && (useAsync=useAsync||true)
                log("execute onBefore "+name);
            }
            if(isFunction(onbeforeEAll)){
                onbeforeEAll.apply(onbeforeEAll,realArguments)
                log("execute onBeforeEvent");
            }
            if(isFunction(onleaveS)){
                tmpFlag=onleaveS.apply(onleaveS,realArguments)
                tmpFlag===false && (canContinueEvent=false)
                tmpFlag===fsmClass.ASYNC && (useAsync=useAsync||true)
                log("execute onLeave "+from);
            }
            if(isFunction(onleaveSAll)){
                onleaveSAll.apply(onleaveSAll,realArguments)
                log("execute onLeaveState");
            }
            if(useAsync===false)
                beginStateChange()
            else{
                tmpcache.isAsync=true
                tmpcache.beginStateChange=beginStateChange
                log("Async : waiting for Manual Transition()");
            }
            function beginStateChange(){
                if(canContinueEvent){
                    FSMInstance.current = to
                    if(isFunction(onenterS)){
                        onenterS.apply(onenterS,realArguments)
                        log("execute onEnterState "+to);
                    }
                    if(isFunction(onenterSAll)){
                        onenterSAll.apply(onenterSAll,realArguments)
                        log("execute onEnterState");
                    }
                    if(isFunction(onafterE)){
                        onafterE.apply(onafterE,realArguments)
                        log("execute onAfter "+name);
                    }
                    if(isFunction(onafterEAll)){
                        onafterEAll.apply(onafterEAll,realArguments)
                        log("execute onAfterEvent");
                    }
                }
                useAsync=false
                tmpcache.isAsync=false
                tmpcache.beginStateChange=null
            }
        }

        /**
         * 默认的Exception处理函数
         * @param eventName
         * @param from
         * @param to
         * @param args
         * @param errorCode
         * @param errorMessage
         */
        function printError(eventName, from, to, args, errorCode, errorMessage){
            log('Exception catched : when execute event ' + eventName + ' : '
                + errorMessage)
        }

        /**
         * 创建FSM，有4种重载形式
         *  create(settingObj)
         *  create(settingObj,ClassName)
         *  create(settingObj,ClassName,onlyClass)
         *  create(ClassName)
         * @returns {null}
         */
        fsmClass.create = function () {
            var createFromExistedFSMClass,settingObj,FSMName,hasParamErr=true,createClassOnly=false
            if(arguments.length==3 && typeof arguments[0] === "object"
                && typeof arguments[1] === "string" && typeof arguments[2] === "boolean"){
                createFromExistedFSMClass=false
                settingObj=arguments[0]
                FSMName=arguments[1]
                createClassOnly=arguments[2]
                hasParamErr=false
            }
            if(arguments.length==2 && typeof arguments[0] === "object" && typeof arguments[1] === "string"){
                createFromExistedFSMClass=false
                settingObj=arguments[0]
                FSMName=arguments[1]
                hasParamErr=false
            }else if(arguments.length === 1){
                switch (typeof arguments[0]){
                    case "object":
                        createFromExistedFSMClass=false;
                        settingObj=arguments[0]
                        hasParamErr=false
                        break;
                    case "string":
                        if(FSMclasses[arguments[0]] !== undefined){
                            createFromExistedFSMClass=true
                            FSMName=arguments[0]
                            settingObj={initial:FSMinitSettings[FSMName]}
                            hasParamErr=false
                        }
                        break;
                }
            }
            if(hasParamErr) {log("FSM.create : error in params") ; return null}
            var userfsmConstructor = createFromExistedFSMClass?FSMclasses[FSMName]:createUserFSM(settingObj),
                complexInitial = typeof settingObj.initial === "object",
                initialState = complexInitial?settingObj.initial.state:settingObj.initial,
                startupEvent = complexInitial?userfsmConstructor.prototype[settingObj.initial.event]:undefined,
                userfsmInstance = createClassOnly?null:new userfsmConstructor(initialState),
                $id = (Math.random()+'').substr(2)
            if(FSMName){
                FSMclasses[FSMName]=userfsmConstructor
                FSMinitSettings[FSMName]=settingObj.initial
            }
            if(createClassOnly===false){
                userfsmInstance.$id = $id
                cache[$id] = {isAsync:false,beginStateChange:null}
                startupEvent && startupEvent.call(userfsmInstance)
                return userfsmInstance;
            }
        }
        fsmClass.FSMs=FSMclasses

        if ( typeof noGlobal !== typeof undefined) {//若noGlobal为undefined，不注册到window
            window.FSM = fsmClass;
        }
        return fsmClass
        /**
         * 寄生组合式继承
         * @param subType
         * @param superType
         */
        function inheritFromSuperType(subType,superType){
            var UserFSM=function(){};//临时构造函数，收集共用方法
            if(typeof subType ==="function" && typeof superType === "function"){
                UserFSM.prototype=superType.prototype
                var prototype=new UserFSM();
                prototype.constructor=subType//修复constructor指向
                subType.prototype=prototype;
            }
        }
        function noop(){}
        function each(array,func){
            if(typeof func === "function" && typeof array === "object"){
                if(array instanceof Array){
                    for(var i= 0,length=array.length, one=array[0];i<length;i++,one = array[i]){
                        if(func(i, one) === false)
                            break;
                    }
                }else{
                    for(var j in array){
                        if(func(j, array[j])===false)
                            break;
                    }
                }
            }
        }
        function isFunction(func){
            return typeof func === "function"
        }
        function log(content){
            debug && console.log(content)
        }
    }))
})(window)
<|endoftext|>


<commit_before>(function() {
    var f = function(selector) {
        var article = $(selector);

        var elem = article;
        while (elem.length && elem.get(0).tagName.toUpperCase() !== 'BODY') {
            elem.css({boxSizing: 'border-box'})
                .css({padding: 5})
                .css({margin: '0 auto'})
                .css({minWidth: 1024})
                .siblings().remove();
            elem = elem.parent();
        }

        return article;
    };

    window.clean = f;
})();<commit_msg>add article clean js without box-sizing<commit_after>(function() {
    var f = function(selector) {
        var article = $(selector);

        var elem = article;
        while (elem.length && elem.get(0).tagName.toUpperCase() !== 'BODY') {
            elem.css({padding: 5})
                .css({margin: '0 auto'})
                .css({minWidth: 1024})
                .siblings().remove();
            elem = elem.parent();
        }

        return article;
    };

    window.clean = f;
})();<|endoftext|>


<commit_before>#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var minimist = require('minimist');
var level = require('level');
var strftime = require('strftime');
var sprintf = require('sprintf');
var through = require('through');

var argv = minimist(process.argv.slice(2));
var HOME = process.env.HOME || process.env.USERDIR;
var datadir = argv.d || path.join(HOME, '.clocker');
mkdirp.sync(datadir);

var db = level(path.join(datadir, 'db'), { encoding: 'json' });

if (argv.h) usage(0)
else if (argv._[0] === 'start') {
    var d = argv.d ? new Date(argv.d) : new Date;
    var type = argv.type || argv.t;
    var pkey = strftime('time!%F %T', d);
    var tkey = 'time-type!' + type + '!' + strftime('%F %T', d);
    db.batch([
        { type: 'put', key: pkey, value: { type: type } },
        { type: 'put', key: tkey, value: 0 }
    ], error);
}
else if (argv._[0] === 'stop') {
    var d = argv.d ? new Date(argv.d) : new Date;
    var s = db.createReadStream({
        gt: 'time!', lt: 'time!~',
        limit: 1, reverse: true
    });
    s.once('data', function (row) {
        row.value.end = strftime('%F %T', d);
        db.put(row.key, row.value, error);
    });
}
else if (argv._[0] === 'status') {
    var s = db.createReadStream({
        gt: 'time!', lt: 'time!~',
        limit: 1, reverse: true
    });
    s.once('data', function (row) {
        var started = new Date(row.key.split('!')[1]);
        if (!row.value.end) {
            var elapsed = (new Date) - started;
            console.log('elapsed time: ' + fmt(elapsed));
        }
        else {
            console.log('stopped');
        }
    });
}
else if (argv._[0] === 'data') {
    var type = argv.type || argv.t || argv._[1];
    var rate = argv.rate || argv.r || argv._[2];
    var title = argv.title || 'consulting';

    var s = db.createReadStream({ gt: 'time!', lt: 'time!~' });
    var rows = [];
    var write = function (row) {
        if (!type) rows.push(row);
        if (row.value.type === type) rows.push(row)
    };
    s.pipe(through(write, function () {
        var hours = rows.reduce(function reducer (acc, row) {
            if (!row.value.end) {
                console.error("clocked time hasn't ended yet");
                return process.exit(1);
            }
            var start = new Date(row.key.split('!')[1]);
            var end = new Date(row.value.end);
            var key = strftime('%F', start);
            if (key !== strftime('%F', end)) {
                var nextDay = new Date(start);
                nextDay.setDate(start.getDate() + 1);
                nextDay.setHours(0);
                nextDay.setMinutes(0);
                nextDay.setSeconds(0);
                nextDay.setMilliseconds(0);

                acc = reducer(acc, {
                    key: 'time!' + strftime('%F %T', nextDay),
                    value: row.value
                });
                end = nextDay;
            }
            var hours = (end - start) / 1000 / 60 / 60;
            if (!acc[key]) {
                acc[key] = {
                    date: strftime('%F', start),
                    hours: 0
                };
            }
            acc[key].hours += hours;
            return acc;
        }, {});

        console.log(JSON.stringify([ {
            title: title,
            rate: rate,
            hours: Object.keys(hours).map(function (key) {
                var h = hours[key];
                return {
                    date: h.date,
                    hours: Number(sprintf('%.1f', h.hours))
                };
            })
        } ], null, 2));
    }));
}
else if (argv._[0] === 'list') {
    var s = db.createReadStream({ gt: 'time!', lt: 'time!~' });
    s.on('error', error);
    s.pipe(through(function (row) {
        var start = new Date(row.key.split('!')[1]);
        var end = row.value.end && new Date(row.value.end);
        var elapsed = (end ? end : new Date) - start;

        console.log(
            '%s  %s  [ %s - %s ]  (%s)%s',
            toStamp(row.key),
            strftime('%F', start),
            strftime('%T', start),
            end ? strftime('%T', end) : 'NOW',
            fmt(elapsed),
            (row.value.type ? '  [' + row.value.type + ']' : '')
        );
    }));
}
else if (argv._[0] === 'get') {
    var key = getKey(argv._[1]);
    db.get(key, function (err, row) {
        if (err) return error(err);
        console.log(row);
    });
}
else if (argv._[0] === 'rm') {
    var key = getKey(argv._[1]);
    db.del(key, error);
}
else if (argv._[0] === 'adjust') {
    if (argv._.length < 4) {
        return error('clocker adjust STAMP {start|end} STAMP');
    }
    var key = getKey(argv._[1]);
    var k = argv._[2];
    var value = argv._.slice(3).join(' ');

    if (k === 'end') {
        db.get(key, function (err, row) {
            if (err) return error(err);
            row[k] = updateDate(value, row[k]);
            db.put(key, row, error);
        });
    }
    else if (k === 'start') {
        db.get(key, function (err, row) {
            if (err) return error(err);
            var newKey = 'time!' + updateDate(value, key.split('!')[1]);

            db.batch([
                { type: 'put', key: newKey, value: row },
                { type: 'del', key: key }
            ], error);
        });
    }
    else return error('clocker adjust STAMP {start|end} STAMP');
}
else if (argv._[0] === 'move') {
    if (argv._.length < 2) {
        return error('clocker move STAMP type');
    }
    var key = getKey(argv._[1]);
    var target = argv._[2];
    db.get(key, function (err, row) {
        if (err) return error(err);
        var prevType = row.type;
        row.type = target;
        db.batch([
            { type: 'del', key: prevType },
            { type: 'put', key: key, value: row }
        ], error);
    });
}
else usage(1)

function usage (code) {
    var rs = fs.createReadStream(__dirname + '/usage.txt');
    rs.pipe(process.stdout);
    rs.on('close', function () {
        if (code) process.exit(code);
    });
}

function pad (s, len) {
    return Array(Math.max(0, len - String(s).length + 1)).join('0') + s;
}

function fmt (elapsed) {
    var n = elapsed / 1000;
    var hh = pad(Math.floor(n / 60 / 60), 2);
    var mm = pad(Math.floor(n / 60 % 60), 2);
    var ss = pad(Math.floor(n % 60), 2);
    return [ hh, mm, ss ].join(':');
}

function error (err) {
    if (!err) return;
    console.error(String(err));
    process.exit(1);
}

function toStamp (s) {
    return Math.floor(new Date(s.split('!')[1]).valueOf() / 1000);
}

function getKey (x) {
    return strftime('time!%F %T', new Date(x * 1000));
}

function updateDate (value, old) {
    var d = new Date(value);
    if (isNaN(d.valueOf())) {
        if (!old || isNaN(old)) {
            old = key.split('!')[1];
        }
        d = new Date(old.split(' ')[0] + ' ' + value);
    }
    return strftime('%F %T', d);
}
<commit_msg>fix for `clocker move` from an undefined type<commit_after>#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var minimist = require('minimist');
var level = require('level');
var strftime = require('strftime');
var sprintf = require('sprintf');
var through = require('through');

var argv = minimist(process.argv.slice(2));
var HOME = process.env.HOME || process.env.USERDIR;
var datadir = argv.d || path.join(HOME, '.clocker');
mkdirp.sync(datadir);

var db = level(path.join(datadir, 'db'), { encoding: 'json' });

if (argv.h) usage(0)
else if (argv._[0] === 'start') {
    var d = argv.d ? new Date(argv.d) : new Date;
    var type = argv.type || argv.t;
    var pkey = strftime('time!%F %T', d);
    var tkey = 'time-type!' + type + '!' + strftime('%F %T', d);
    db.batch([
        { type: 'put', key: pkey, value: { type: type } },
        { type: 'put', key: tkey, value: 0 }
    ], error);
}
else if (argv._[0] === 'stop') {
    var d = argv.d ? new Date(argv.d) : new Date;
    var s = db.createReadStream({
        gt: 'time!', lt: 'time!~',
        limit: 1, reverse: true
    });
    s.once('data', function (row) {
        row.value.end = strftime('%F %T', d);
        db.put(row.key, row.value, error);
    });
}
else if (argv._[0] === 'status') {
    var s = db.createReadStream({
        gt: 'time!', lt: 'time!~',
        limit: 1, reverse: true
    });
    s.once('data', function (row) {
        var started = new Date(row.key.split('!')[1]);
        if (!row.value.end) {
            var elapsed = (new Date) - started;
            console.log('elapsed time: ' + fmt(elapsed));
        }
        else {
            console.log('stopped');
        }
    });
}
else if (argv._[0] === 'data') {
    var type = argv.type || argv.t || argv._[1];
    var rate = argv.rate || argv.r || argv._[2];
    var title = argv.title || 'consulting';

    var s = db.createReadStream({ gt: 'time!', lt: 'time!~' });
    var rows = [];
    var write = function (row) {
        if (!type) rows.push(row);
        if (row.value.type === type) rows.push(row)
    };
    s.pipe(through(write, function () {
        var hours = rows.reduce(function reducer (acc, row) {
            if (!row.value.end) {
                console.error("clocked time hasn't ended yet");
                return process.exit(1);
            }
            var start = new Date(row.key.split('!')[1]);
            var end = new Date(row.value.end);
            var key = strftime('%F', start);
            if (key !== strftime('%F', end)) {
                var nextDay = new Date(start);
                nextDay.setDate(start.getDate() + 1);
                nextDay.setHours(0);
                nextDay.setMinutes(0);
                nextDay.setSeconds(0);
                nextDay.setMilliseconds(0);

                acc = reducer(acc, {
                    key: 'time!' + strftime('%F %T', nextDay),
                    value: row.value
                });
                end = nextDay;
            }
            var hours = (end - start) / 1000 / 60 / 60;
            if (!acc[key]) {
                acc[key] = {
                    date: strftime('%F', start),
                    hours: 0
                };
            }
            acc[key].hours += hours;
            return acc;
        }, {});

        console.log(JSON.stringify([ {
            title: title,
            rate: rate,
            hours: Object.keys(hours).map(function (key) {
                var h = hours[key];
                return {
                    date: h.date,
                    hours: Number(sprintf('%.1f', h.hours))
                };
            })
        } ], null, 2));
    }));
}
else if (argv._[0] === 'list') {
    var s = db.createReadStream({ gt: 'time!', lt: 'time!~' });
    s.on('error', error);
    s.pipe(through(function (row) {
        var start = new Date(row.key.split('!')[1]);
        var end = row.value.end && new Date(row.value.end);
        var elapsed = (end ? end : new Date) - start;

        console.log(
            '%s  %s  [ %s - %s ]  (%s)%s',
            toStamp(row.key),
            strftime('%F', start),
            strftime('%T', start),
            end ? strftime('%T', end) : 'NOW',
            fmt(elapsed),
            (row.value.type ? '  [' + row.value.type + ']' : '')
        );
    }));
}
else if (argv._[0] === 'get') {
    var key = getKey(argv._[1]);
    db.get(key, function (err, row) {
        if (err) return error(err);
        console.log(row);
    });
}
else if (argv._[0] === 'rm') {
    var key = getKey(argv._[1]);
    db.del(key, error);
}
else if (argv._[0] === 'adjust') {
    if (argv._.length < 4) {
        return error('clocker adjust STAMP {start|end} STAMP');
    }
    var key = getKey(argv._[1]);
    var k = argv._[2];
    var value = argv._.slice(3).join(' ');

    if (k === 'end') {
        db.get(key, function (err, row) {
            if (err) return error(err);
            row[k] = updateDate(value, row[k]);
            db.put(key, row, error);
        });
    }
    else if (k === 'start') {
        db.get(key, function (err, row) {
            if (err) return error(err);
            var newKey = 'time!' + updateDate(value, key.split('!')[1]);

            db.batch([
                { type: 'put', key: newKey, value: row },
                { type: 'del', key: key }
            ], error);
        });
    }
    else return error('clocker adjust STAMP {start|end} STAMP');
}
else if (argv._[0] === 'move') {
    if (argv._.length < 2) {
        return error('clocker move STAMP type');
    }
    var key = getKey(argv._[1]);
    var target = argv._[2];
    db.get(key, function (err, row) {
        if (err) return error(err);
        var prevType = row.type;
        row.type = target;
        db.batch([
            prevType && { type: 'del', key: prevType },
            { type: 'put', key: key, value: row }
        ].filter(Boolean), error);
    });
}
else usage(1)

function usage (code) {
    var rs = fs.createReadStream(__dirname + '/usage.txt');
    rs.pipe(process.stdout);
    rs.on('close', function () {
        if (code) process.exit(code);
    });
}

function pad (s, len) {
    return Array(Math.max(0, len - String(s).length + 1)).join('0') + s;
}

function fmt (elapsed) {
    var n = elapsed / 1000;
    var hh = pad(Math.floor(n / 60 / 60), 2);
    var mm = pad(Math.floor(n / 60 % 60), 2);
    var ss = pad(Math.floor(n % 60), 2);
    return [ hh, mm, ss ].join(':');
}

function error (err) {
    if (!err) return;
    console.error(String(err));
    process.exit(1);
}

function toStamp (s) {
    return Math.floor(new Date(s.split('!')[1]).valueOf() / 1000);
}

function getKey (x) {
    return strftime('time!%F %T', new Date(x * 1000));
}

function updateDate (value, old) {
    var d = new Date(value);
    if (isNaN(d.valueOf())) {
        if (!old || isNaN(old)) {
            old = key.split('!')[1];
        }
        d = new Date(old.split(' ')[0] + ' ' + value);
    }
    return strftime('%F %T', d);
}
<|endoftext|>


<commit_before>// These events are run when the DOM is ready
$( document ).ready(function() {

  if (Modernizr.localstorage) {
    // This browser supports HTML5 so we can store the players
    // in the browser localstorage.
    if (localStorage.length == 0)
    {
      initialiseLocalStorage();
    }
    readPlayers();
  }
  else
  {
    // no native support for HTML5 storage :(
    // maybe try dojox.storage or a third-party solution
    alert("HTML5 localtstorage not supported =(")
  }

  setPlayerDivDimensions();
  initialiseEventHandlers();

});

function initialiseLocalStorage()
{
  localStorage.setItem('webplayer.counter', 0);
}

function toggleTogglerAndForm()
{
  $("#newPlayerForm").trigger("reset");
  $("#newPlayerForm").slideToggle(200);
  $(".toggler").slideToggle(200);
}

function initialiseEventHandlers()
{

  // We use jQuery UI Dialog to show the add player form
  // and should be able to reuse most of this code for editing
  // existing players
  $( "#newPlayerForm" ).dialog({
     autoOpen: false,
     modal: true,
     appendTo: "#player",
     width: "auto",
     dialogClass: "addPlayer",
     position: { my: "center", at: "center", of: window },
     buttons: {
       "Add player": function() {
           addNewPlayer($("#name").val(), $("#address").val());
           $( this ).dialog( "close" );
       },
       Cancel: function() {
         $( this ).dialog( "close" );
       }
     },
     open: function() {
        $('.ui-widget-overlay').addClass('darker-overlay');
     },
     close: function() {
       allFields = $( [] ).add( "#name" ).add( "#address" );
       allFields.val( "" );
       $('.ui-widget-overlay').addClass('darker-overlay');
     }
   });

   $( "#new-player" )
     .button()
     .click(function() {
       $( "#newPlayerForm" ).dialog( "open" );
     });
}

// Resize the player whenever the window is resized.
$(window).resize(function() {
  setPlayerDivDimensions();
});

function loadPlayer(sender) {
  // Load a player into the iframe
  // TODO: Fix focus so <space> can pause
  var iframe = document.getElementById("playerframe");
  var purl = sender.currentTarget.parentNode.childNodes.item(1).textContent;
  iframe.src = purl;
  if (purl.search("soundcloud.com") > 0)
  {
    // TODO: Autoplay on load
    iframe.src = "http://w.soundcloud.com/player/?url="+purl;
    soundcloudWidget = SC.Widget(iframe);
    soundcloudWidget.load(purl, {'auto_play': true});
  }
  document.title = "Webplayer - " + sender.currentTarget.parentNode.childNodes.item(0).textContent;
}

function setPlayerDivDimensions()
{
  var width = document.body.clientWidth;
  // playerList = 300px and has 2 x 10px paddings
  width = width - 320;

  $("#player").css("width", width);
  $("#playerframe").css("width", width);

  // TODO: Set div and frame height more correctly than this hack.
  $("#player").css("height", document.height);
  $("#playerFrame").css("height", document.height);
}

function resetPlaylistAttributes()
{
  $("li").hoverIntent({
    over: slideDownwards,
    interval: 1000,
    out: slideUpwards
  });

  $(".tools-delete").click(function(event) {
    deletePlayer(event);
  });

  $(".tools-edit").click(function(event) {
    editPlayer(event);
  });

  // This binds actions to the new
 elements that make them open the players
  $(".playerlink").on("click", function(event) {
    loadPlayer(event);
  });
}

function slideDownwards()
{
  $(this).find(".tools").slideDown(200);
}

function slideUpwards()
{
  $(this).find(".tools").slideUp(200);
}

function editPlayer(sender)
{

  // TODO: Javascript prompts are ugly. Make this beautiful. fancybox?

  var parentNode = sender.currentTarget.parentNode;
  var name = prompt("Enter a name", parentNode.parentNode.childNodes.item(0).childNodes.item(0).textContent);
    var key = parentNode.parentNode.childNodes.item(0).childNodes.item(2).textContent;
  if (key != null && name != null)
  {
    localStorage.setItem("webplayer.players."+key+".name", name);
    readPlayers();
  }
}

function readPlayers()
{
  // Read the saved players from the localstorage and populate the
  // playerlist with the stored metadata
  // TODO: set

  var values = [];

  var counter = parseInt(localStorage["webplayer.counter"])
  for (var i = 0; i < counter; i++)
  {
    var exists = localStorage.getItem("webplayer.players."+i+".name");
    if (exists != null)
    {
      values.push({name: localStorage.getItem("webplayer.players."+i+".name"),
                   url: localStorage.getItem("webplayer.players."+i+".url"),
                   id: i});
    }
  }

  var options = {
    item: '

EditDelete

'
  };

  var playerList = new List('playerlist', options, values);
  resetPlaylistAttributes();
}

function addNewPlayer(name, url)
{
  // TODO: Scrape the Bandcamp  to extract a reference to the playlist ID
  //       so we can embedd HTML5 players instead of full pages inside the
  //       IFRAME
  // TODO: Make this modular so it can effectively cope with adding resources from
  //       different domains.
  if (Modernizr.localstorage)
  {
    if (url.length == 0)
    {
      return;
    }

    var counter = parseInt(localStorage.getItem("webplayer.counter"));
    if (name.length > 0)
    {
      localStorage.setItem("webplayer.players." + counter+".name", name);
    }
    else
    {
      localStorage.setItem("webplayer.players." + counter+".name", "No name");
    }
    localStorage.setItem("webplayer.players." + counter+".added", new Date());
    localStorage.setItem("webplayer.players." + counter+".url", url);
    localStorage.setItem("webplayer.counter", counter + 1);
  }
  readPlayers();
  return false;
}

function deletePlayer(sender)
{
  if (Modernizr.localstorage)
  {
    var key = sender.currentTarget.parentNode.parentNode.childNodes.item(0).childNodes.item(2).textContent;
    if (key != null)
    {
      localStorage.removeItem("webplayer.players."+key+".name");
      localStorage.removeItem("webplayer.players."+key+".added");
      localStorage.removeItem("webplayer.players."+key+".url");
      readPlayers();
     }
  }
}
<commit_msg>Preventing the Add Player interface from being resizable.<commit_after>// These events are run when the DOM is ready
$( document ).ready(function() {

  if (Modernizr.localstorage) {
    // This browser supports HTML5 so we can store the players
    // in the browser localstorage.
    if (localStorage.length == 0)
    {
      initialiseLocalStorage();
    }
    readPlayers();
  }
  else
  {
    // no native support for HTML5 storage :(
    // maybe try dojox.storage or a third-party solution
    alert("HTML5 localtstorage not supported =(")
  }

  setPlayerDivDimensions();
  initialiseEventHandlers();

});

function initialiseLocalStorage()
{
  localStorage.setItem('webplayer.counter', 0);
}

function toggleTogglerAndForm()
{
  $("#newPlayerForm").trigger("reset");
  $("#newPlayerForm").slideToggle(200);
  $(".toggler").slideToggle(200);
}

function initialiseEventHandlers()
{

  // We use jQuery UI Dialog to show the add player form
  // and should be able to reuse most of this code for editing
  // existing players
  $( "#newPlayerForm" ).dialog({
     autoOpen: false,
     modal: true,
     appendTo: "#player",
     width: "auto",
     dialogClass: "addPlayer",
     position: { my: "center", at: "center", of: window },
     resizable: false,
     buttons: {
       "Add player": function() {
           addNewPlayer($("#name").val(), $("#address").val());
           $( this ).dialog( "close" );
       },
       Cancel: function() {
         $( this ).dialog( "close" );
       }
     },
     open: function() {
        $('.ui-widget-overlay').addClass('darker-overlay');
     },
     close: function() {
       allFields = $( [] ).add( "#name" ).add( "#address" );
       allFields.val( "" );
       $('.ui-widget-overlay').addClass('darker-overlay');
     }
   });

   $( "#new-player" )
     .button()
     .click(function() {
       $( "#newPlayerForm" ).dialog( "open" );
     });
}

// Resize the player whenever the window is resized.
$(window).resize(function() {
  setPlayerDivDimensions();
});

function loadPlayer(sender) {
  // Load a player into the iframe
  // TODO: Fix focus so <space> can pause
  var iframe = document.getElementById("playerframe");
  var purl = sender.currentTarget.parentNode.childNodes.item(1).textContent;
  iframe.src = purl;
  if (purl.search("soundcloud.com") > 0)
  {
    // TODO: Autoplay on load
    iframe.src = "http://w.soundcloud.com/player/?url="+purl;
    soundcloudWidget = SC.Widget(iframe);
    soundcloudWidget.load(purl, {'auto_play': true});
  }
  document.title = "Webplayer - " + sender.currentTarget.parentNode.childNodes.item(0).textContent;
}

function setPlayerDivDimensions()
{
  var width = document.body.clientWidth;
  // playerList = 300px and has 2 x 10px paddings
  width = width - 320;

  $("#player").css("width", width);
  $("#playerframe").css("width", width);

  // TODO: Set div and frame height more correctly than this hack.
  $("#player").css("height", document.height);
  $("#playerFrame").css("height", document.height);
}

function resetPlaylistAttributes()
{
  $("li").hoverIntent({
    over: slideDownwards,
    interval: 1000,
    out: slideUpwards
  });

  $(".tools-delete").click(function(event) {
    deletePlayer(event);
  });

  $(".tools-edit").click(function(event) {
    editPlayer(event);
  });

  // This binds actions to the new
 elements that make them open the players
  $(".playerlink").on("click", function(event) {
    loadPlayer(event);
  });
}

function slideDownwards()
{
  $(this).find(".tools").slideDown(200);
}

function slideUpwards()
{
  $(this).find(".tools").slideUp(200);
}

function editPlayer(sender)
{

  // TODO: Javascript prompts are ugly. Make this beautiful. fancybox?

  var parentNode = sender.currentTarget.parentNode;
  var name = prompt("Enter a name", parentNode.parentNode.childNodes.item(0).childNodes.item(0).textContent);
    var key = parentNode.parentNode.childNodes.item(0).childNodes.item(2).textContent;
  if (key != null && name != null)
  {
    localStorage.setItem("webplayer.players."+key+".name", name);
    readPlayers();
  }
}

function readPlayers()
{
  // Read the saved players from the localstorage and populate the
  // playerlist with the stored metadata
  // TODO: set

  var values = [];

  var counter = parseInt(localStorage["webplayer.counter"])
  for (var i = 0; i < counter; i++)
  {
    var exists = localStorage.getItem("webplayer.players."+i+".name");
    if (exists != null)
    {
      values.push({name: localStorage.getItem("webplayer.players."+i+".name"),
                   url: localStorage.getItem("webplayer.players."+i+".url"),
                   id: i});
    }
  }

  var options = {
    item: '

EditDelete

'
  };

  var playerList = new List('playerlist', options, values);
  resetPlaylistAttributes();
}

function addNewPlayer(name, url)
{
  // TODO: Scrape the Bandcamp  to extract a reference to the playlist ID
  //       so we can embedd HTML5 players instead of full pages inside the
  //       IFRAME
  // TODO: Make this modular so it can effectively cope with adding resources from
  //       different domains.
  if (Modernizr.localstorage)
  {
    if (url.length == 0)
    {
      return;
    }

    var counter = parseInt(localStorage.getItem("webplayer.counter"));
    if (name.length > 0)
    {
      localStorage.setItem("webplayer.players." + counter+".name", name);
    }
    else
    {
      localStorage.setItem("webplayer.players." + counter+".name", "No name");
    }
    localStorage.setItem("webplayer.players." + counter+".added", new Date());
    localStorage.setItem("webplayer.players." + counter+".url", url);
    localStorage.setItem("webplayer.counter", counter + 1);
  }
  readPlayers();
  return false;
}

function deletePlayer(sender)
{
  if (Modernizr.localstorage)
  {
    var key = sender.currentTarget.parentNode.parentNode.childNodes.item(0).childNodes.item(2).textContent;
    if (key != null)
    {
      localStorage.removeItem("webplayer.players."+key+".name");
      localStorage.removeItem("webplayer.players."+key+".added");
      localStorage.removeItem("webplayer.players."+key+".url");
      readPlayers();
     }
  }
}
<|endoftext|>


<commit_before>var app = {};

// $(function() { //when DOM is ready...
	app.users = new UserCollection([

		{username:'Jennifer'},
		{username:'Jason'},
		{username:'Ty'}
	]);

var LoginModel = Backbone.Model.extend({
	// // defaults : {"username" : ""},
	// replace : function(usr){
	// this.set("username", usr);
	// }
});

//LOGIN VIEW---------------------------------------------------------

var LoginView = Backbone.View.extend({
	render : function (){
		console.log("render login");
		var login = '<button id="login" type="submit">Login</button>';
		var usrBtn = '<button id="newName">Add Name</button>';
		var input = '
<input type="text" id="userField" value="Add New User Name">';
		var headline = '
To Do List
';
		var headline2 = '
Please log in.
';
		var nametag = "
name:
";
		var selector = this.userSelector();
		this.$el.html("

" + headline + headline2 + nametag + selector + login + input + usrBtn + "
");
		// this.el.append(this.$el);
	},
    userSelector: function(){
		var users = [];
		var content = "";

		for (var i = 0;i < app.users.length; i++){
			users.push(app.users.models[i].attributes.username);
			content+=("<option>"+users[i]+"</option>");

      	}
		content = "<select id='selectDropdown'>"+content+"</select>";
		return content;
	},

	addNewUser: function(){
		var newUser = $("#userField").val();
		app.users.add({username: newUser});
		this.render();
	},

	events: {
		"click #login" 		: "userLogin",
		"click #newName"	: "addNewUser"
	},

	userLogin : function(){
		$("#login-area").hide();
		userView.render();
 	}

 });

	var UserModel = Backbone.Model.extend({
	    defaults : {"value" : ""},
	    replace : function (str) {
	      this.set("value", str);
	    }
	});

// ----------------------------------------------------------------
//USERVIEW---------------------------------------------------------
// ----------------------------------------------------------------

 var UserView = Backbone.View.extend({
 	render: function (){
 		console.log('rendering user view');
 		var userVal = this.model.get("creator");
		var greeting = "
 Hello, " + userVal + "!
"
 		var btn = '<button id="createTasks">List of Tasks</button>';
 		var input = '<input type="text" value= "Enter another task"/>';
 		this.$el.html("

"+greeting+"
");
		this.$("#app").append(this.el);
 		console.log('userView render works!');

 	},

 	initialize: function(){
 		this.model.on("change", this.render, this);
 	},
 // 	events: {
 // 		"click #login" : "render"
 // 	},
 // 	taskList : function(){
 // 		this.taskView.add({});
 // }

 });

 // initialize: function(){
 //       this.model.on("change", this.render, this);
 // 	// console.log("init login")
 // 	// this.once("change", this.render, this);
 // },
 // events: {
 // 	"click #login" 		: "userLogin",
 // 	"click #newName"	: "addNewUser"
 // },

	// userLogin : function(){
	// 	loginView.remove();
	// 	taskView.render();
	// }

// });

var UserModel = Backbone.Model.extend({
    defaults : {"value" : ""},
    replace : function (str) {
      this.set("value", str);
    }
});

var UserView = Backbone.View.extend({
	render: function (){
		console.log('rendering user view');
		var userVal = this.model.get("creator");
		var greeting= "
 Hello, "+ userVal +" !!
";
		var btn = '<button id="createTasks">List of Tasks</button>';
		var input = '<input type="text" value= "Enter another task"/>';
		this.$el.html("
"+greeting+"
");
		$('#app').append(this.el);
		console.log('user works!',this.el);

	},

	initialize: function(){
		this.model.on("change", this.render, this);
	},

	events: {
		"click #login" : "render"
	}
	// taskList : function(){
	// 	this.taskView.add({});
	// }

});

var TaskModel = Backbone.Model.extend({
    defaults : {"value" : ""},
    replace : function (str) {
      this.set("value", str);
    }
});

var TaskView = Backbone.View.extend({
	render: function (){
		console.log('rendering task view');
		var taskVal = this.model.get("title");
		var btn = '<button id="showTasks">Add New Task</button>';
		var input = '<input type="text" value= "Create A New Task"/>';
		this.$el.html("

"+taskVal+"
");
		console.log('it works!');

	},

	initialize: function(){
		this.model.on("change", this.render, this);
	},

	events: {
		"click #login" : "render"
	},
	taskList : function(){
		this.taskView.add({});
	}

});

var TaskCollection = Backbone.Collection.extend({
    model : TaskModel
});

var TaskCollectionView = Backbone.View.extend({
    render : function () {
    	console.log('Rendering!');
        var btn = '<button id="addTask">Add Task</button>';
        var div = '

';
        this.$el.html(div + btn);
},

   initialize: function () {
   	console.log('BLAH!');
   	this.listenTo(this.collection, "add", this.addView);
   },
   events : {
   	"click #addTask" : "addModel",
   },
   addModel : function(){
   	this.collection.add({});
   },
    addView : function (newModel) {
    	console.log('New Model');
        // newModel.set("value","Enter New Task");
        var view = new TaskView({model : newModel});
        view.render();
        this.$("#taskList").append(view.$el);
    }

  });
// });

var loginView;
var userView;
var userModel;
var taskView;
var taskCollection;
var testModel;
var taskCollectionView;
$(document).ready( function () {
	console.log("ready");

userModel = new UserModel({title:'Make this page work!',
                           description: 'Fix all the code!',
                           creator: 'Jennifer'});

loginView = new LoginView({el:"#app"});
loginView.render();

userModel = new UserModel({title:'Make this page work!',
                           description: 'Fix all the code!',
                           creator: 'Jennifer'});

userView = new UserView({ model: userModel});
// userView.render();

// userModel = new UserModel({creator: 'Jennifer'});
// userView = new UserView({el: "#app"})

taskCollection = new TaskCollection();
taskView = new TaskView({model: testModel});
taskCollectionView = new TaskCollectionView({ collection : taskCollection});
taskCollectionView.render();
taskCollection.add(testModel)

// taskView.render();

$("#app").append(userView.$el);

});

// 	 app.tasks = new TaskCollection([
// 		// test data here
// 	]);

// 	app.gui = new GUI(app.users,
// 						app.tasks,
// 						'#app');// selector of main div
// });
<commit_msg>fixing conflicts<commit_after>var app = {};

// $(function() { //when DOM is ready...
	app.users = new UserCollection([

		{username:'Jennifer'},
		{username:'Jason'},
		{username:'Ty'}
	]);

var LoginModel = Backbone.Model.extend({
	// // defaults : {"username" : ""},
	// replace : function(usr){
	// this.set("username", usr);
	// }
});

//LOGIN VIEW---------------------------------------------------------

var LoginView = Backbone.View.extend({
	render : function (){
		console.log("render login");
		var login = '<button id="login" type="submit">Login</button>';
		var usrBtn = '<button id="newName">Add Name</button>';
		var input = '
<input type="text" id="userField" value="Add New User Name">';
		var headline = '
To Do List
';
		var headline2 = '
Please log in.
';
		var nametag = "
name:
";
		var selector = this.userSelector();
		this.$el.html("

" + headline + headline2 + nametag + selector + login + input + usrBtn + "
");
		// this.el.append(this.$el);
	},
    userSelector: function(){
		var users = [];
		content = "";

		for (var i = 0;i < app.users.length; i++){
			users.push(app.users.models[i].attributes.username);
			content+=("<option>"+users[i]+"</option>");

      	}
		content = "<select id='selectDropdown'>"+content+"</select>";
		return content;
	},

	addNewUser: function(){
		var newUser = $("#userField").val();
		app.users.add({username: newUser});
		this.render();
	},

	events: {
		"click #login" 		: "userLogin",
		"click #newName"	: "addNewUser"
	},

	userLogin : function(){
		$("#login-area").hide();
		userView.render();
 	}

 });

	var UserModel = Backbone.Model.extend({
	    defaults : {"value" : ""},
	    replace : function (str) {
	      this.set("value", str);
	    }
	});

// ----------------------------------------------------------------
//USERVIEW---------------------------------------------------------
// ----------------------------------------------------------------

 var UserView = Backbone.View.extend({
 	render: function (){
 		console.log('rendering user view');
 		var userVal = this.model.get("creator");
		var greeting = "
 Hello, " + userVal + "!
"
 		var btn = '<button id="createTasks">List of Tasks</button>';
 		var input = '<input type="text" value= "Enter another task"/>';
 		this.$el.html("

"+greeting+"
");
		this.$("#app").append(this.el);
 		console.log('userView render works!');

 	},

 	initialize: function(){
 		this.model.on("change", this.render, this);
 	},
 // 	events: {
 // 		"click #login" : "render"
 // 	},
 // 	taskList : function(){
 // 		this.taskView.add({});
 // }
 });
 // initialize: function(){
 //       this.model.on("change", this.render, this);
 // 	// console.log("init login")
 // 	// this.once("change", this.render, this);
 // },
 // events: {
 // 	"click #login" 		: "userLogin",
 // 	"click #newName"	: "addNewUser"
 // },

	// userLogin : function(){
	// 	loginView.remove();
	// 	taskView.render();
	// }

// });
var UserModel = Backbone.Model.extend({
    defaults : {"value" : ""},
    replace : function (str) {
      this.set("value", str);
    }
});

var UserView = Backbone.View.extend({
	render: function (){
		console.log('rendering user view');
		var userVal = this.model.get("creator");
		var greeting= "
 Hello, "+ userVal +" !!
";
		var btn = '<button id="createTasks">List of Tasks</button>';
		var input = '<input type="text" value= "Enter another task"/>';
		this.$el.html("
"+greeting+"
");
		$('#app').append(this.el);
		console.log('user works!',this.el);

	},

	initialize: function(){
		this.model.on("change", this.render, this);
	},

	events: {
		"click #login" : "render"
	}
	// taskList : function(){
	// 	this.taskView.add({});
	// }

});

var TaskModel = Backbone.Model.extend({
    defaults : {"value" : ""},
    replace : function (str) {
      this.set("value", str);
    }
});

// var TaskView = Backbone.View.extend({
// 	render: function (){
// 		console.log('rendering task view');
// 		var taskVal = this.model.get("title");
// 		var btn = '<button id="showTasks">Add New Task</button>';
// 		var input = '<input type="text" value= "Create A New Task"/>';
// 		this.$el.html("

"+taskVal+"
");
// 		console.log('it works!');

// 	},

// 	initialize: function(){
// 		this.model.on("change", this.render, this);
// 	},

// 	events: {
// 		"click #login" : "render"
// 	},
// 	taskList : function(){
// 		this.taskView.add({});
// 	}

// });

// var TaskCollection = Backbone.Collection.extend({
//     model : TaskModel
// });

// var TaskCollectionView = Backbone.View.extend({
//     render : function () {
//     	console.log('Rendering!');
//         var btn = '<button id="addTask">Add Task</button>';
//         var div = '

';
//         this.$el.html(div + btn);
// },

//    initialize: function () {
//    	console.log('BLAH!');
//    	this.listenTo(this.collection, "add", this.addView);
//    },
//    events : {
//    	"click #addTask" : "addModel",
//    },
//    addModel : function(){
//    	this.collection.add({});
//    },
//     addView : function (newModel) {
//     	console.log('New Model');
//         // newModel.set("value","Enter New Task");
//         var view = new TaskView({model : newModel});
//         view.render();
//         this.$("#taskList").append(view.$el);
//     }

//   });
// });
var content;
var loginView;
var userView;
var userModel;
var taskView;
var taskCollection;
var testModel;
var taskCollectionView;
$(document).ready( function () {
	console.log("ready");

userModel = new UserModel({title:'Make this page work!',
                           description: 'Fix all the code!',
                           creator: 'Jennifer'});

loginView = new LoginView({el:"#app"});
loginView.render();

userModel = new UserModel({title:'Make this page work!',
                           description: 'Fix all the code!',
                           creator: app.users});

userView = new UserView({ model: userModel});
// userView.render();

// userModel = new UserModel({creator: 'Jennifer'});
// userView = new UserView({el: "#app"})

// taskCollection = new TaskCollection();
// taskView = new TaskView({model: testModel});
// taskCollectionView = new TaskCollectionView({ collection : taskCollection});
// taskCollectionView.render();
// taskCollection.add(testModel)

// taskView.render();

$("#app").append(userView.$el);

});

// 	 app.tasks = new TaskCollection([
// 		// test data here
// 	]);

// 	app.gui = new GUI(app.users,
// 						app.tasks,
// 						'#app');// selector of main div
// });
<|endoftext|>


<commit_before>const settings = {
    animationTimeInMs: 250,
    animationType: 'linear', // Can be: 'linear' or 'ease'
    colors: [
        'blue',
        'red',
        'green',
        'navy',
        'orange',
        'pink',
        'brown',
        'dark_green',
        'lilac',
        'army',
    ]
};

const makeCollapser = function (color, width, height) {
    let collapser = document.createElement('div');
    collapser.className = `collapser ${color}`;
    collapser.setAttribute(
        'style',
        `width: ${width.toString()}px; height: calc(100% - ${height.toString()}px);`
    );

    collapser.addEventListener('click', toggleCollapse);

    return collapser;
};

const makeExpander = function (collapsed) {
    let expander = document.createElement('a');
    expander.href = 'javascript:void(0)';
    expander.className = 'expander';
    let expanderText = document.createTextNode(collapsed ? '[+]' : '[-]');
    expander.appendChild(expanderText);

    expander.addEventListener('click', toggleCollapse);

    return expander;
};

const addCollapser = function (comment) {
    let numChildComments = comment.querySelectorAll(':scope > .child .comment').length;

    if (numChildComments === 0) return;

    let isDeleted = comment.classList.contains('deleted');
    let isCollapsed = comment.classList.contains('collapsed');
    let depth = 0;
    let currentComment = comment;

    while (currentComment.closest('.comment') !== null) {
        depth++;
        currentComment = currentComment.parentNode;
    }

    let anchorEl = comment.querySelector('.midcol');

    let color = settings.colors[depth % 10];
    let width = anchorEl.offsetWidth;
    let height = isDeleted ? 30 : anchorEl.offsetHeight;

    let collapser = makeCollapser(color, width, height);
    anchorEl.appendChild(collapser);

    let tagline = comment.querySelector(':scope > .entry .tagline');
    let expander = makeExpander(isCollapsed);
    tagline.insertBefore(expander, tagline.firstChild);

    let toRemoveEl = comment.querySelector(':scope > .entry .tagline .expand');

    if (toRemoveEl) toRemoveEl.remove();
};

const toggleCollapse = function (e) {
    let comment = e.target.closest('.comment');

    if (comment.classList.contains('collapsed')) uncollapse(comment);
    else collapse(comment);
};

const uncollapse = function (comment) {
    comment.querySelector('.child').style.display = 'block';
    comment.querySelector('.midcol').style.display = 'block';
    comment.classList.remove('collapsed');
    comment.classList.add('noncollapsed');
    comment.querySelector('.expander').innerHTML = '[-]';
};

const collapse = function (commentTree) {
    let parentComment = commentTree.querySelector(':scope > .entry')

    // Only change scroll position if the parent comment is not entirely in
    // viewport
    if (!elementInViewport(parentComment)) {
        // Padding is so that the scroll position isn't directly on the edge of
        // the collapsed comment
        let padding = 10;
        let distanceFromTop = 0;
        let commentContext = commentTree;
        if (commentContext.offsetParent) {
            do {
                distanceFromTop += commentContext.offsetTop;
                commentContext = commentContext.offsetParent;
            } while (commentContext);
        }

        smoothScroll(distanceFromTop - padding);
    }

    // Set the height to a fixed value in order to animate it later
    let childToHide = commentTree.querySelector('.child');
    childToHide.style.overflow = 'hidden';
    childToHide.style.transition = `height ${settings.animationTimeInMs.toString()}ms`;
    childToHide.style.height = `${childToHide.offsetHeight.toString()}px`;

    // This will now trigger the animated hide
    // Waiting a moment first to hopefully ensure that the above properties are
    // applied
    setTimeout(function () {
        childToHide.style.height = '0';

        // Not using the transitionstart and transitionend events here as they
        // refused to work, except when changing the CSS property value in the
        // Chrome dev tools...

        // This looks better if it happens before the animation is complete
        setTimeout(function () {
            commentTree.querySelector('.midcol').style.display = 'none';
            commentTree.querySelector('.expander').innerHTML = '[+]';
            commentTree.classList.remove('noncollapsed');
            commentTree.classList.add('collapsed');
        }, settings.animationTimeInMs - 100);

        setTimeout(function () {
            childToHide.style.display = 'none';
            childToHide.style.height = 'auto'; // For future (un)collapsing of this element
        }, settings.animationTimeInMs);
    }, 50);
};

// Based on: https://github.com/alicelieutier/smoothScroll
const smoothScroll = function (destination) {
    let getComputedPosition = function (start, destination, elapsed) {
        // Cubic easing algorithm
        let easeInOutCubic = function (t) {
            return t < .5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1;
        };

        if (settings.animationType === 'ease') {
            return elapsed > settings.animationTimeInMs ? destination : start + (destination - start) * easeInOutCubic(elapsed / settings.animationTimeInMs);
        } else {
            // This will also be used for 'linear' option
            return elapsed > settings.animationTimeInMs ? destination : start + (destination - start) * (elapsed / settings.animationTimeInMs);
        }
    };

    let start = window.scrollY;
    let clock = Date.now();

    let requestAnimationFrame = window.requestAnimationFrame;

    let step = function () {
        let elapsed = Date.now() - clock;

        window.scroll(0, getComputedPosition(start, destination, elapsed));

        if (elapsed <= settings.animationTimeInMs) requestAnimationFrame(step);
    };

    step();
};

// Test whether a given element is visible in the viewport
const elementInViewport = function (el) {
    let rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.right <= window.innerWidth &&
        rect.bottom <= window.innerHeight
    );
};

// Watch for any new comments that are loaded and add collapsers to them too
let observer = new MutationObserver(function (mutations) {
    let once = false;

    mutations.forEach(function (mutation) {
      // Only continue if mutation is to tree of nodes
        if (mutation.type !== 'childList') return;

        Array.from(mutation.addedNodes).forEach(function (node) {
            // Only continue if node is an element
            if (node.nodeType !== 1 || !node.classList.contains('comment')) return;

            if (!once) {
                once = true;

                addCollapser(node.parentNode.parentNode.parentNode);
            }

            addCollapser(node);
        });
    });
});

observer.observe(document, {
    subtree: true,
    childList: true
});

// Add a collapser div to every non-deleted comment
let comments = Array.from(document.querySelectorAll('.comment')).reverse();

const createCollapsers = function () {
    let comment = comments.pop();

    if (!comment) return false;

    addCollapser(comment);

    requestAnimationFrame(function () {
        createCollapsers();
    });
};

createCollapsers();
<commit_msg>improve brevity/readability<commit_after>const settings = {
    animationTimeInMs: 250,
    colors: [
        'blue',
        'red',
        'green',
        'navy',
        'orange',
        'pink',
        'brown',
        'dark_green',
        'lilac',
        'army',
    ]
};

function makeCollapser(color, width, height) {
    let collapser = document.createElement('div');
    collapser.className = `collapser ${color}`;
    collapser.setAttribute(
        'style',
        `width: ${width}px; height: calc(100% - ${height}px);`
    );

    collapser.addEventListener('click', toggleCollapse);

    return collapser;
}

function makeExpander(collapsed) {
    let expander = document.createElement('a');
    expander.href = 'javascript:void(0)';
    expander.className = 'expander';
    let expanderText = document.createTextNode(collapsed ? '[+]' : '[-]');
    expander.appendChild(expanderText);

    expander.addEventListener('click', toggleCollapse);

    return expander;
};

function addCollapser(comment) {
    let numChildComments = comment.querySelectorAll(
        ':scope > .child .comment'
    ).length;

    if (numChildComments === 0) {
        return;
    }

    let isDeleted = comment.classList.contains('deleted');
    let isCollapsed = comment.classList.contains('collapsed');

    let depth = 0;
    let currentComment = comment;
    while (currentComment.closest('.comment') !== null) {
        depth++;
        currentComment = currentComment.parentNode;
    }

    let anchorEl = comment.querySelector('.midcol');

    let color = settings.colors[depth % 10];
    let width = anchorEl.offsetWidth;
    let height = isDeleted ? 30 : anchorEl.offsetHeight;

    let collapser = makeCollapser(color, width, height);
    anchorEl.appendChild(collapser);

    let tagline = comment.querySelector(':scope > .entry .tagline');
    let expander = makeExpander(isCollapsed);
    tagline.insertBefore(expander, tagline.firstChild);

    let nativeExpander = comment.querySelector(
        ':scope > .entry .tagline .expand'
    );
    if (nativeExpander) {
        nativeExpander.remove();
    }
}

function toggleCollapse(e) {
    let comment = e.target.closest('.comment');

    if (comment.classList.contains('collapsed')) {
        uncollapse(comment);
    } else {
        collapse(comment);
    }
}

function uncollapse(comment) {
    comment.querySelector('.child').style.display = 'block';
    comment.querySelector('.midcol').style.display = 'block';
    comment.classList.remove('collapsed');
    comment.classList.add('noncollapsed');
    comment.querySelector('.expander').innerHTML = '[-]';
}

function collapse(commentTree) {
    let parentComment = commentTree.querySelector(':scope > .entry')

    if (!elementInViewport(parentComment)) {
        smoothScroll(parentComment.offsetParent.offsetTop - 10);
    }

    // Set the height to a fixed value in order to animate it later
    let elementToHide = commentTree.querySelector('.child');
    elementToHide.style.overflow = 'hidden';
    elementToHide.style.transition = `height ${settings.animationTimeInMs}ms`;
    elementToHide.style.height = `${elementToHide.offsetHeight}px`;

    // Delay start of animation, otherwise the above properties may not have
    // been set yet
    setTimeout(function() {
        elementToHide.style.height = '0';

        setTimeout(function () {
            commentTree.querySelector('.midcol').style.display = 'none';
            commentTree.querySelector('.expander').innerHTML = '[+]';
            commentTree.classList.remove('noncollapsed');
            commentTree.classList.add('collapsed');
        }, settings.animationTimeInMs - 100);

        setTimeout(function () {
            elementToHide.style.display = 'none';
            elementToHide.style.height = 'auto'; // prevent animation on uncollapse
        }, settings.animationTimeInMs);
    }, 50);
}

// Based on: https://github.com/alicelieutier/smoothScroll
function smoothScroll(destination) {
    let getComputedPosition = function (startScroll, destination, elapsed) {
        if (elapsed > settings.animationTimeInMs) {
            return destination;
        } else {
            return (
                startScroll + (destination - startScroll) *
                (elapsed / settings.animationTimeInMs)
            );
        }
    };

    let startScroll = window.scrollY;
    let startTime = Date.now();

    let step = function () {
        let elapsed = Date.now() - startTime;

        scroll(0, getComputedPosition(startScroll, destination, elapsed));

        if (elapsed <= settings.animationTimeInMs) {
            requestAnimationFrame(step);
        }
    };

    step();
}

function elementInViewport(el) {
    let rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.right <= window.innerWidth &&
        rect.bottom <= window.innerHeight
    );
}

// Watch for any new comments that are loaded and add collapsers to them too
let observer = new MutationObserver(function (mutations) {
    let once = false;

    mutations.forEach(function (mutation) {
      // Only continue if mutation is to tree of nodes
        if (mutation.type !== 'childList') return;

        Array.from(mutation.addedNodes).forEach(function (node) {
            // Only continue if node is an element
            if (node.nodeType !== 1 || !node.classList.contains('comment')) return;

            if (!once) {
                once = true;

                addCollapser(node.parentNode.parentNode.parentNode);
            }

            addCollapser(node);
        });
    });
});

observer.observe(document, {
    subtree: true,
    childList: true
});

// Add a collapser div to every non-deleted comment
let comments = Array.from(document.querySelectorAll('.comment')).reverse();

function createCollapsers() {
    let comment = comments.pop();

    if (!comment) {
        return false;
    }

    addCollapser(comment);

    requestAnimationFrame(function () {
        createCollapsers();
    });
}

createCollapsers();
<|endoftext|>