<commit_before>/**
 *
 * @copyright Copyright (c) 2021, Daniel Calviño Sánchez (danxuliu@gmail.com)
 *
 * @license AGPL-3.0-or-later
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

import UAParser from 'ua-parser-js'

import { mediaDevicesManager } from '../../webrtc/index'

import TrackSource from './TrackSource'

/**
 * Source node to get audio and video tracks from MediaDevicesManager.
 *
 * Two output tracks with "audio" and "video" as ids are provided.
 *
 * The source is started by calling "start(constraints)". Whether the audio
 * track, video track or both start depends on the given constraints and the
 * current devices. Even if no track is started at first the node will listen to
 * changes in the audioInputId and videoInputId attributes from the
 * MediaDevicesManager and start, stop and replace the tracks are needed.
 *
 * Once the source is started "stop()" needs to be called to stop listening to
 * changes in the devices. Stopping the source also stops any track currently
 * active.
 *
 *        --------------------
 *       |                    | --->
 *       | MediaDevicesSource |
 *       |                    | --->
 *        --------------------
 */
export default class MediaDevicesSource extends TrackSource {

	constructor(outputCount) {
		super()

		this._addOutputTrackSlot('audio')
		this._addOutputTrackSlot('video')

		this._handleAudioInputIdChangedBound = this._handleAudioInputIdChanged.bind(this)
		this._handleVideoInputIdChangedBound = this._handleVideoInputIdChanged.bind(this)
	}

	async start(constraints, retryNoVideoCallback) {
		// Try to get the devices list before getting user media.
		mediaDevicesManager.enableDeviceEvents()
		mediaDevicesManager.disableDeviceEvents()

		// The handlers for "change:audioInputId" and "change:videoInputId"
		// events expect the initial "getUserMedia" call to have been completed
		// before being used, so they must be set once the media has started.

		let stream
		let error

		[stream, error] = await this._startAudioAndVideo(constraints)

		// Fallback for users without a camera or with a camera that can not be
		// accessed, but only if audio is meant to be used.
		if (error && constraints.audio !== false && constraints.video !== false) {
			retryNoVideoCallback(constraints, error);

			[stream, error] = await this._startAudioOnly(constraints)
		}

		if (error) {
			// No media could be got, but the node is active nevertheless and
			// listening to device changes until explicitly stopped.
			mediaDevicesManager.on('change:audioInputId', this._handleAudioInputIdChangedBound)
			mediaDevicesManager.on('change:videoInputId', this._handleVideoInputIdChangedBound)

			throw error
		}

		// According to the specification "getUserMedia()" will return at
		// most a single track of each kind.
		const audioTrack = stream.getAudioTracks().length > 0 ? stream.getAudioTracks()[0] : null
		if (stream.getAudioTracks().length > 1) {
			console.error('More than a single audio track returned by getUserMedia, only the first one will be used')
		}

		const videoTrack = stream.getVideoTracks().length > 0 ? stream.getVideoTracks()[0] : null
		if (stream.getVideoTracks().length > 1) {
			console.error('More than a single video track returned by getUserMedia, only the first one will be used')
		}

		this._setOutputTrack('audio', audioTrack)
		this._setOutputTrack('video', videoTrack)

		mediaDevicesManager.on('change:audioInputId', this._handleAudioInputIdChangedBound)
		mediaDevicesManager.on('change:videoInputId', this._handleVideoInputIdChangedBound)
	}

	async _startAudioAndVideo(constraints) {
		this._adjustVideoConstraintsForChromium(constraints)

		let stream

		try {
			stream = await mediaDevicesManager.getUserMedia(constraints)
		} catch (error) {
			return [null, error]
		}

		// Although the promise should be resolved only if all the constraints
		// are met Edge resolves it if both audio and video are requested but
		// only audio is available.
		if (constraints.video && stream.getVideoTracks().length === 0) {
			return [null, Error('Video expected but not received')]
		}

		return [stream, null]
	}

	async _startAudioOnly(constraints) {
		constraints.video = false

		let stream

		try {
			stream = await mediaDevicesManager.getUserMedia(constraints)
		} catch (error) {
			return [null, error]
		}

		return [stream, null]
	}

	stop() {
		if (this.getOutputTrack('audio')) {
			this.getOutputTrack('audio').stop()
			this._setOutputTrack('audio', null)
		}

		if (this.getOutputTrack('video')) {
			this.getOutputTrack('video').stop()
			this._setOutputTrack('video', null)
		}

		mediaDevicesManager.off('change:audioInputId', this._handleAudioInputIdChangedBound)
		mediaDevicesManager.off('change:videoInputId', this._handleVideoInputIdChangedBound)
	}

	/**
	 * Adjusts video constraints to work around bug in Chromium.
	 *
	 * In Chromium it is not possible to increase the resolution of a track once
	 * it has been cloned, so the track needs to be initialized with a high
	 * resolution (otherwise real devices are initialized with a resolution
	 * around 640x480). Therefore, the video is requested with a loose
	 * constraint for a high resolution, so if the camera does not have such
	 * resolution it will still return the highest resolution available without
	 * failing.
	 *
	 * A high frame rate needs to be requested too, as some cameras offer high
	 * resolution but with low frame rates, so Chromium could end providing a
	 * laggy high resolution video. If the frame rate is requested too then
	 * Chromium needs to balance all the constraints and thus provide a video
	 * without the highest resolution but with an acceptable frame rate.
	 *
	 * @param {object} constraints the constraints to be adjusted
	 */
	_adjustVideoConstraintsForChromium(constraints) {
		const parser = new UAParser()
		const browserName = parser.getBrowser().name

		if (browserName !== 'Chrome'
			&& browserName !== 'Chromium'
			&& browserName !== 'Opera'
			&& browserName !== 'Safari'
			&& browserName !== 'Mobile Safari'
			&& browserName !== 'Edge') {
			return
		}

		if (!constraints.video) {
			return
		}

		if (!(constraints.video instanceof Object)) {
			constraints.video = {}
		}

		constraints.video.width = 1920
		constraints.video.height = 1200
		constraints.video.frameRate = 60
	}

	_handleAudioInputIdChanged(mediaDevicesManager, audioInputId) {
		if (this._pendingAudioInputIdChangedCount) {
			this._pendingAudioInputIdChangedCount++

			return
		}

		this._pendingAudioInputIdChangedCount = 1

		const resetPendingAudioInputIdChangedCount = () => {
			const audioInputIdChangedAgain = this._pendingAudioInputIdChangedCount > 1

			this._pendingAudioInputIdChangedCount = 0

			if (audioInputIdChangedAgain) {
				this._handleAudioInputIdChanged(mediaDevicesManager.get('audioInputId'))
			}
		}

		if (audioInputId === null) {
			if (this.getOutputTrack('audio')) {
				this.getOutputTrack('audio').stop()
			}
			this._setOutputTrack('audio', null)

			resetPendingAudioInputIdChangedCount()

			return
		}

		if (this.getOutputTrack('audio')) {
			const settings = this.getOutputTrack('audio').getSettings()
			if (settings && settings.deviceId === audioInputId) {
				return
			}
		}

		mediaDevicesManager.getUserMedia({ audio: true }).then(stream => {
			// According to the specification "getUserMedia({ audio: true })" will
			// return a single audio track.
			const track = stream.getTracks()[0]
			if (stream.getTracks().length > 1) {
				console.error('More than a single audio track returned by getUserMedia, only the first one will be used')
			}

			if (this.getOutputTrack('audio')) {
				this.getOutputTrack('audio').stop()
			}
			this._setOutputTrack('audio', track)

			resetPendingAudioInputIdChangedCount()
		}).catch(() => {
			if (this.getOutputTrack('audio')) {
				this.getOutputTrack('audio').stop()
			}
			this._setOutputTrack('audio', null)

			resetPendingAudioInputIdChangedCount()
		})
	}

	_handleVideoInputIdChanged(mediaDevicesManager, videoInputId) {
		if (this._pendingVideoInputIdChangedCount) {
			this._pendingVideoInputIdChangedCount++

			return
		}

		this._pendingVideoInputIdChangedCount = 1

		const resetPendingVideoInputIdChangedCount = () => {
			const videoInputIdChangedAgain = this._pendingVideoInputIdChangedCount > 1

			this._pendingVideoInputIdChangedCount = 0

			if (videoInputIdChangedAgain) {
				this._handleVideoInputIdChanged(mediaDevicesManager.get('videoInputId'))
			}
		}

		if (videoInputId === null) {
			if (this.getOutputTrack('video')) {
				this.getOutputTrack('video').stop()
			}
			this._setOutputTrack('video', null)

			resetPendingVideoInputIdChangedCount()

			return
		}

		if (this.getOutputTrack('video')) {
			const settings = this.getOutputTrack('video').getSettings()
			if (settings && settings.deviceId === videoInputId) {
				return
			}
		}

		const constraints = { video: true }
		this._adjustVideoConstraintsForChromium(constraints)

		mediaDevicesManager.getUserMedia(constraints).then(stream => {
			// According to the specification "getUserMedia({ video: true })" will
			// return a single video track.
			const track = stream.getTracks()[0]
			if (stream.getTracks().length > 1) {
				console.error('More than a single video track returned by getUserMedia, only the first one will be used')
			}

			if (this.getOutputTrack('video')) {
				this.getOutputTrack('video').stop()
			}
			this._setOutputTrack('video', track)

			resetPendingVideoInputIdChangedCount()
		}).catch(() => {
			if (this.getOutputTrack('video')) {
				this.getOutputTrack('video').stop()
			}
			this._setOutputTrack('video', null)

			resetPendingVideoInputIdChangedCount()
		})
	}

}
<commit_msg>Fix media never requested again when changing again to current device<commit_after>/**
 *
 * @copyright Copyright (c) 2021, Daniel Calviño Sánchez (danxuliu@gmail.com)
 *
 * @license AGPL-3.0-or-later
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

import UAParser from 'ua-parser-js'

import { mediaDevicesManager } from '../../webrtc/index'

import TrackSource from './TrackSource'

/**
 * Source node to get audio and video tracks from MediaDevicesManager.
 *
 * Two output tracks with "audio" and "video" as ids are provided.
 *
 * The source is started by calling "start(constraints)". Whether the audio
 * track, video track or both start depends on the given constraints and the
 * current devices. Even if no track is started at first the node will listen to
 * changes in the audioInputId and videoInputId attributes from the
 * MediaDevicesManager and start, stop and replace the tracks are needed.
 *
 * Once the source is started "stop()" needs to be called to stop listening to
 * changes in the devices. Stopping the source also stops any track currently
 * active.
 *
 *        --------------------
 *       |                    | --->
 *       | MediaDevicesSource |
 *       |                    | --->
 *        --------------------
 */
export default class MediaDevicesSource extends TrackSource {

	constructor(outputCount) {
		super()

		this._addOutputTrackSlot('audio')
		this._addOutputTrackSlot('video')

		this._handleAudioInputIdChangedBound = this._handleAudioInputIdChanged.bind(this)
		this._handleVideoInputIdChangedBound = this._handleVideoInputIdChanged.bind(this)
	}

	async start(constraints, retryNoVideoCallback) {
		// Try to get the devices list before getting user media.
		mediaDevicesManager.enableDeviceEvents()
		mediaDevicesManager.disableDeviceEvents()

		// The handlers for "change:audioInputId" and "change:videoInputId"
		// events expect the initial "getUserMedia" call to have been completed
		// before being used, so they must be set once the media has started.

		let stream
		let error

		[stream, error] = await this._startAudioAndVideo(constraints)

		// Fallback for users without a camera or with a camera that can not be
		// accessed, but only if audio is meant to be used.
		if (error && constraints.audio !== false && constraints.video !== false) {
			retryNoVideoCallback(constraints, error);

			[stream, error] = await this._startAudioOnly(constraints)
		}

		if (error) {
			// No media could be got, but the node is active nevertheless and
			// listening to device changes until explicitly stopped.
			mediaDevicesManager.on('change:audioInputId', this._handleAudioInputIdChangedBound)
			mediaDevicesManager.on('change:videoInputId', this._handleVideoInputIdChangedBound)

			throw error
		}

		// According to the specification "getUserMedia()" will return at
		// most a single track of each kind.
		const audioTrack = stream.getAudioTracks().length > 0 ? stream.getAudioTracks()[0] : null
		if (stream.getAudioTracks().length > 1) {
			console.error('More than a single audio track returned by getUserMedia, only the first one will be used')
		}

		const videoTrack = stream.getVideoTracks().length > 0 ? stream.getVideoTracks()[0] : null
		if (stream.getVideoTracks().length > 1) {
			console.error('More than a single video track returned by getUserMedia, only the first one will be used')
		}

		this._setOutputTrack('audio', audioTrack)
		this._setOutputTrack('video', videoTrack)

		mediaDevicesManager.on('change:audioInputId', this._handleAudioInputIdChangedBound)
		mediaDevicesManager.on('change:videoInputId', this._handleVideoInputIdChangedBound)
	}

	async _startAudioAndVideo(constraints) {
		this._adjustVideoConstraintsForChromium(constraints)

		let stream

		try {
			stream = await mediaDevicesManager.getUserMedia(constraints)
		} catch (error) {
			return [null, error]
		}

		// Although the promise should be resolved only if all the constraints
		// are met Edge resolves it if both audio and video are requested but
		// only audio is available.
		if (constraints.video && stream.getVideoTracks().length === 0) {
			return [null, Error('Video expected but not received')]
		}

		return [stream, null]
	}

	async _startAudioOnly(constraints) {
		constraints.video = false

		let stream

		try {
			stream = await mediaDevicesManager.getUserMedia(constraints)
		} catch (error) {
			return [null, error]
		}

		return [stream, null]
	}

	stop() {
		if (this.getOutputTrack('audio')) {
			this.getOutputTrack('audio').stop()
			this._setOutputTrack('audio', null)
		}

		if (this.getOutputTrack('video')) {
			this.getOutputTrack('video').stop()
			this._setOutputTrack('video', null)
		}

		mediaDevicesManager.off('change:audioInputId', this._handleAudioInputIdChangedBound)
		mediaDevicesManager.off('change:videoInputId', this._handleVideoInputIdChangedBound)
	}

	/**
	 * Adjusts video constraints to work around bug in Chromium.
	 *
	 * In Chromium it is not possible to increase the resolution of a track once
	 * it has been cloned, so the track needs to be initialized with a high
	 * resolution (otherwise real devices are initialized with a resolution
	 * around 640x480). Therefore, the video is requested with a loose
	 * constraint for a high resolution, so if the camera does not have such
	 * resolution it will still return the highest resolution available without
	 * failing.
	 *
	 * A high frame rate needs to be requested too, as some cameras offer high
	 * resolution but with low frame rates, so Chromium could end providing a
	 * laggy high resolution video. If the frame rate is requested too then
	 * Chromium needs to balance all the constraints and thus provide a video
	 * without the highest resolution but with an acceptable frame rate.
	 *
	 * @param {object} constraints the constraints to be adjusted
	 */
	_adjustVideoConstraintsForChromium(constraints) {
		const parser = new UAParser()
		const browserName = parser.getBrowser().name

		if (browserName !== 'Chrome'
			&& browserName !== 'Chromium'
			&& browserName !== 'Opera'
			&& browserName !== 'Safari'
			&& browserName !== 'Mobile Safari'
			&& browserName !== 'Edge') {
			return
		}

		if (!constraints.video) {
			return
		}

		if (!(constraints.video instanceof Object)) {
			constraints.video = {}
		}

		constraints.video.width = 1920
		constraints.video.height = 1200
		constraints.video.frameRate = 60
	}

	_handleAudioInputIdChanged(mediaDevicesManager, audioInputId) {
		if (this._pendingAudioInputIdChangedCount) {
			this._pendingAudioInputIdChangedCount++

			return
		}

		this._pendingAudioInputIdChangedCount = 1

		const resetPendingAudioInputIdChangedCount = () => {
			const audioInputIdChangedAgain = this._pendingAudioInputIdChangedCount > 1

			this._pendingAudioInputIdChangedCount = 0

			if (audioInputIdChangedAgain) {
				this._handleAudioInputIdChanged(mediaDevicesManager.get('audioInputId'))
			}
		}

		if (audioInputId === null) {
			if (this.getOutputTrack('audio')) {
				this.getOutputTrack('audio').stop()
			}
			this._setOutputTrack('audio', null)

			resetPendingAudioInputIdChangedCount()

			return
		}

		if (this.getOutputTrack('audio')) {
			const settings = this.getOutputTrack('audio').getSettings()
			if (settings && settings.deviceId === audioInputId) {
				resetPendingAudioInputIdChangedCount()

				return
			}
		}

		mediaDevicesManager.getUserMedia({ audio: true }).then(stream => {
			// According to the specification "getUserMedia({ audio: true })" will
			// return a single audio track.
			const track = stream.getTracks()[0]
			if (stream.getTracks().length > 1) {
				console.error('More than a single audio track returned by getUserMedia, only the first one will be used')
			}

			if (this.getOutputTrack('audio')) {
				this.getOutputTrack('audio').stop()
			}
			this._setOutputTrack('audio', track)

			resetPendingAudioInputIdChangedCount()
		}).catch(() => {
			if (this.getOutputTrack('audio')) {
				this.getOutputTrack('audio').stop()
			}
			this._setOutputTrack('audio', null)

			resetPendingAudioInputIdChangedCount()
		})
	}

	_handleVideoInputIdChanged(mediaDevicesManager, videoInputId) {
		if (this._pendingVideoInputIdChangedCount) {
			this._pendingVideoInputIdChangedCount++

			return
		}

		this._pendingVideoInputIdChangedCount = 1

		const resetPendingVideoInputIdChangedCount = () => {
			const videoInputIdChangedAgain = this._pendingVideoInputIdChangedCount > 1

			this._pendingVideoInputIdChangedCount = 0

			if (videoInputIdChangedAgain) {
				this._handleVideoInputIdChanged(mediaDevicesManager.get('videoInputId'))
			}
		}

		if (videoInputId === null) {
			if (this.getOutputTrack('video')) {
				this.getOutputTrack('video').stop()
			}
			this._setOutputTrack('video', null)

			resetPendingVideoInputIdChangedCount()

			return
		}

		if (this.getOutputTrack('video')) {
			const settings = this.getOutputTrack('video').getSettings()
			if (settings && settings.deviceId === videoInputId) {
				resetPendingVideoInputIdChangedCount()

				return
			}
		}

		const constraints = { video: true }
		this._adjustVideoConstraintsForChromium(constraints)

		mediaDevicesManager.getUserMedia(constraints).then(stream => {
			// According to the specification "getUserMedia({ video: true })" will
			// return a single video track.
			const track = stream.getTracks()[0]
			if (stream.getTracks().length > 1) {
				console.error('More than a single video track returned by getUserMedia, only the first one will be used')
			}

			if (this.getOutputTrack('video')) {
				this.getOutputTrack('video').stop()
			}
			this._setOutputTrack('video', track)

			resetPendingVideoInputIdChangedCount()
		}).catch(() => {
			if (this.getOutputTrack('video')) {
				this.getOutputTrack('video').stop()
			}
			this._setOutputTrack('video', null)

			resetPendingVideoInputIdChangedCount()
		})
	}

}
<|endoftext|>


<commit_before>/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global require, yo, define, brackets: true, $, jQuery, window, document, navigator*/

/**************
 *   @author : Ajain Vivek
 *   @desc : Yo.js - Templating Utility Library
 *   @license : MIT Licensed
 ***************/

(function (w, doc, un) {
    "use strict";

    window.yo.utils = window.yo.utils || {};

    yo.utils.template = yo.utils.template || {};

    var baseURL = "";

    //Set the base URL for template class
    var setBaseURL = function (url) {
        baseURL = url;
    };

    //Get the base URL
    var getBaseURL = function () {
        return baseURL;
    };

    //Cache the dom reference
    var cachedDOM = function (uid, root, ele, template) {

        var cacheObj = {
            uid: uid,
            ele: ele,
            template: template
        };

        var cacheId = cacheObj.uid;
        var setCache = true;
        var i;

        cachedDOM.cache = cachedDOM.cache || {};

        if (cachedDOM.cache[root] === null || cachedDOM.cache[root] === undefined) {
            cachedDOM.cache[root] = [];
        } else {
            for (i = 0; i < cachedDOM.cache[root].length; i++) {
                if (cachedDOM.cache[root][i].ele === ele) {
                    cacheId = cachedDOM.cache[root][i].uid;
                    setCache = false;
                }
            }
        }

        if (setCache) {
            cachedDOM.cache[root].push(cacheObj);
        }

        return {
            uid : cacheId
        };
    };

    //Fetch the HTML Template
    var fetchHTML = function (fileRef) {
        var ele = "";
        var url = baseURL + fileRef;
        $.ajax({
            url: url,
            type: 'get',
            dataType: 'html',
            async: false,
            cache: false,
            success: function (html) {
                ele = html;
            },
            fail: function () {
                console.error("Error: source not found");
            }
        });

        return ele;
    };

    //Map the string with curly braces
    var getValues = function (str) {
        var vals = [], // an array to collect the strings that are found
            rxp = new RegExp("{([^}]+)}", "g"),
            curMatch;

        while ((curMatch = rxp.exec(str)) !== null) {
            vals.push(curMatch[1]);
        }

        return vals;
    };

    //Add the element on to the dom
    var add = function (options, callback) {

        var defaults = {
            root: options.root || "body",
            ele: options.ele || "",
            template: options.template || "",
            animate: options.animate || "slide",
            mode: options.mode || "append",
            data: options.data || {}
        };

        var uid = (new Date()).getTime();

        var keys = Object.keys(defaults.data);
        var html = fetchHTML(defaults.template);
        var template = html;
        var arrValues = getValues(html);
        var i, j;

        var cacheDOM = cachedDOM(uid, defaults.root, defaults.ele, defaults.template);

        //Set the data value for dom element
        for (i = 0; i < arrValues.length; i++) {
            for (j = 0; j < keys.length; j++) {
                if (arrValues[i] === keys[j]) {
                    html = html.replace("{" + keys[j] + "}", defaults.data[keys[j]]);
                }
            }
        }

        //Remove Hidden Element if it exists in the DOM
        $("#templateBuilder").remove();

        var hiddenEle = $("body").append("

");
        var ele = [];
        var arrEl = [];

        if (defaults.ele instanceof Array) {
            for (i = 0; i < defaults.ele.length; i++) {
                if (defaults.ele[i].charAt(0) === "#" || defaults.ele[i].charAt(0) === ".") {
                    arrEl = $("#templateBuilder").html(html).find(defaults.ele[i]); //Insert into the dom
                    arrEl.first().attr("uid", cacheDOM.uid);
                    ele.push(arrEl[0]);
                } else {
                    console.error("Error: Incorrect reference - pass class or id only.");
                }
            }
        } else {
            if (defaults.ele.charAt(0) === "#" || defaults.ele.charAt(0) === ".") {
                ele = $("#templateBuilder").html(html).find(defaults.ele); //Insert into the dom
                ele.first().attr("uid", cacheDOM.uid);
            } else {
                console.error("Error: Incorrect reference - pass class or id only.");
            }
        }

        if (defaults.mode === "insert") {
            $(defaults.root).html(ele);
        } else {
            $(defaults.root).append(ele);
        }

        var oRef = {
            uid: cacheDOM.uid,
            template: defaults.template,
            ele: defaults.ele
        };

        if (typeof callback === "function") {
            callback(oRef); // Execute callback function
        }

        return oRef;

    };

    //Get cached data
    var cache = function () {
        return cachedDOM.cache;
    };

    //Remove from the dom but will not be removed from cache
    var remove = function (options, callback) {

        var defaults = {
            root: options.root || "body",
            ele: options.ele || "",
            uid: options.uid || "",
            animate: options.animate || "slide"
        };
        var i, j;
        var cachedDom = cache();
        var keys = Object.keys(cachedDom);

        if (defaults.uid !== "") {
            for (i = 0; i < keys.length; i++) {
                for (j = 0; j < cachedDom[keys[i]].length; j++) {
                    if (defaults.uid === cachedDom[keys[i]][j].uid) {
                        $(keys[i]).find("[uid='" + defaults.uid + "']").remove();
                        break;
                    }
                }
            }

        } else {
            if (defaults.ele.charAt(0) === "#" || defaults.ele.charAt(0) === ".") {
                $(defaults.ele).remove();
            }
        }

    };

    //Destroy the element from dom
    var destroy = function (options, callback) {

        var defaults = {
            root: options.root || "body",
            ele: options.ele || "",
            uid: options.uid || "",
            animate: options.animate || "slide"
        };

        var i, j;
        var cachedDom = cache();
        var keys = Object.keys(cachedDom);

        if (defaults.uid !== "") {
            for (i = 0; i < keys.length; i++) {
                for (j = 0; j < cachedDom[keys[i]].length; j++) {
                    if (defaults.uid === cachedDom[keys[i]][j].uid) {
                        $(keys[i]).find("[uid='" + defaults.uid + "']").remove();
                        cachedDom[keys[i]].splice(j, 1); //Remove from cache
                        break;
                    }
                }
            }

        } else {
            if (defaults.ele.charAt(0) === "#" || defaults.ele.charAt(0) === ".") {

                for (i = 0; i < keys.length; i++) {
                    for (j = 0; j < cachedDom[keys[i]].length; j++) {
                        if (defaults.ele === cachedDom[keys[i]][j].ele) {
                            cachedDom[keys[i]].splice(j, 1); //Remove from cache
                        }
                    }
                }

                $(defaults.ele).remove();

            } else {
                console.error("Error: Incorrect reference - pass class or id only.");
            }
        }

    };

    yo.utils.template = {
        add: add,
        remove: remove,
        destroy: destroy,
        cache: cache,
        setBaseURL: setBaseURL,
        getBaseURL: getBaseURL
    };

    return yo.utils.template;

}(window, document, undefined));<commit_msg>Template Utility: Callback Return Object Update<commit_after>/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global require, yo, define, brackets: true, $, jQuery, window, document, navigator*/

/**************
 *   @author : Ajain Vivek
 *   @desc : Yo.js - Templating Utility Library
 *   @license : MIT Licensed
 ***************/

(function (w, doc, un) {
    "use strict";

    window.yo.utils = window.yo.utils || {};

    yo.utils.template = yo.utils.template || {};

    var baseURL = "";

    //Set the base URL for template class
    var setBaseURL = function (url) {
        baseURL = url;
    };

    //Get the base URL
    var getBaseURL = function () {
        return baseURL;
    };

    //Cache the dom reference
    var cachedDOM = function (uid, root, ele, template) {

        var cacheObj = {
            uid: uid,
            ele: ele,
            template: template
        };

        var cacheId = cacheObj.uid;
        var setCache = true;
        var i;

        cachedDOM.cache = cachedDOM.cache || {};

        if (cachedDOM.cache[root] === null || cachedDOM.cache[root] === undefined) {
            cachedDOM.cache[root] = [];
        } else {
            for (i = 0; i < cachedDOM.cache[root].length; i++) {
                if (cachedDOM.cache[root][i].ele === ele) {
                    cacheId = cachedDOM.cache[root][i].uid;
                    setCache = false;
                }
            }
        }

        if (setCache) {
            cachedDOM.cache[root].push(cacheObj);
        }

        return {
            uid : cacheId
        };
    };

    //Fetch the HTML Template
    var fetchHTML = function (fileRef) {
        var ele = "";
        var url = baseURL + fileRef;
        $.ajax({
            url: url,
            type: 'get',
            dataType: 'html',
            async: false,
            cache: false,
            success: function (html) {
                ele = html;
            },
            fail: function () {
                console.error("Error: source not found");
            }
        });

        return ele;
    };

    //Map the string with curly braces
    var getValues = function (str) {
        var vals = [], // an array to collect the strings that are found
            rxp = new RegExp("{([^}]+)}", "g"),
            curMatch;

        while ((curMatch = rxp.exec(str)) !== null) {
            vals.push(curMatch[1]);
        }

        return vals;
    };

    //Add the element on to the dom
    var add = function (options, callback) {

        var defaults = {
            root: options.root || "body",
            ele: options.ele || "",
            template: options.template || "",
            animate: options.animate || "slide",
            mode: options.mode || "append",
            data: options.data || {}
        };

        var uid = (new Date()).getTime();

        var keys = Object.keys(defaults.data);
        var html = fetchHTML(defaults.template);
        var template = html;
        var arrValues = getValues(html);
        var i, j;

        var cacheDOM = cachedDOM(uid, defaults.root, defaults.ele, defaults.template);

        //Set the data value for dom element
        for (i = 0; i < arrValues.length; i++) {
            for (j = 0; j < keys.length; j++) {
                if (arrValues[i] === keys[j]) {
                    html = html.replace("{" + keys[j] + "}", defaults.data[keys[j]]);
                }
            }
        }

        //Remove Hidden Element if it exists in the DOM
        $("#templateBuilder").remove();

        var hiddenEle = $("body").append("

");
        var ele = [];
        var arrEl = [];

        if (defaults.ele instanceof Array) {
            for (i = 0; i < defaults.ele.length; i++) {
                if (defaults.ele[i].charAt(0) === "#" || defaults.ele[i].charAt(0) === ".") {
                    arrEl = $("#templateBuilder").html(html).find(defaults.ele[i]); //Insert into the dom
                    arrEl.first().attr("uid", cacheDOM.uid);
                    ele.push(arrEl[0]);
                } else {
                    console.error("Error: Incorrect reference - pass class or id only.");
                }
            }
        } else {
            if (defaults.ele.charAt(0) === "#" || defaults.ele.charAt(0) === ".") {
                ele = $("#templateBuilder").html(html).find(defaults.ele); //Insert into the dom
                ele.first().attr("uid", cacheDOM.uid);
            } else {
                console.error("Error: Incorrect reference - pass class or id only.");
            }
        }

        if (defaults.mode === "insert") {
            $(defaults.root).html(ele);
        } else {
            $(defaults.root).append(ele);
        }

        var oRef = {
            uid: ("[uid='" + cacheDOM.uid + "']"),
            template: defaults.template,
            ele: defaults.ele
        };

        if (typeof callback === "function") {
            callback(oRef); // Execute callback function
        }

        return {
            uid: cacheDOM.uid,
            template: defaults.template,
            ele: defaults.ele
        };

    };

    //Get cached data
    var cache = function () {
        return cachedDOM.cache;
    };

    //Remove from the dom but will not be removed from cache
    var remove = function (options, callback) {

        var defaults = {
            root: options.root || "body",
            ele: options.ele || "",
            uid: options.uid || "",
            animate: options.animate || "slide"
        };
        var i, j;
        var cachedDom = cache();
        var keys = Object.keys(cachedDom);

        if (defaults.uid !== "") {
            for (i = 0; i < keys.length; i++) {
                for (j = 0; j < cachedDom[keys[i]].length; j++) {
                    if (defaults.uid === cachedDom[keys[i]][j].uid) {
                        $(keys[i]).find("[uid='" + defaults.uid + "']").remove();
                        break;
                    }
                }
            }

        } else {
            if (defaults.ele.charAt(0) === "#" || defaults.ele.charAt(0) === ".") {
                $(defaults.ele).remove();
            }
        }

    };

    //Destroy the element from dom
    var destroy = function (options, callback) {

        var defaults = {
            root: options.root || "body",
            ele: options.ele || "",
            uid: options.uid || "",
            animate: options.animate || "slide"
        };

        var i, j;
        var cachedDom = cache();
        var keys = Object.keys(cachedDom);

        if (defaults.uid !== "") {
            for (i = 0; i < keys.length; i++) {
                for (j = 0; j < cachedDom[keys[i]].length; j++) {
                    if (defaults.uid === cachedDom[keys[i]][j].uid) {
                        $(keys[i]).find("[uid='" + defaults.uid + "']").remove();
                        cachedDom[keys[i]].splice(j, 1); //Remove from cache
                        break;
                    }
                }
            }

        } else {
            if (defaults.ele.charAt(0) === "#" || defaults.ele.charAt(0) === ".") {

                for (i = 0; i < keys.length; i++) {
                    for (j = 0; j < cachedDom[keys[i]].length; j++) {
                        if (defaults.ele === cachedDom[keys[i]][j].ele) {
                            cachedDom[keys[i]].splice(j, 1); //Remove from cache
                        }
                    }
                }

                $(defaults.ele).remove();

            } else {
                console.error("Error: Incorrect reference - pass class or id only.");
            }
        }

    };

    yo.utils.template = {
        add: add,
        remove: remove,
        destroy: destroy,
        cache: cache,
        setBaseURL: setBaseURL,
        getBaseURL: getBaseURL
    };

    return yo.utils.template;

}(window, document, undefined));<|endoftext|>


<commit_before>/**
 * @aside guide forms
 *
 * This is a specialized field which shows a {@link Ext.ux.picker.DateTime} when tapped. If it has a predefined value,
 * or a value is selected in the {@link Ext.ux.picker.DateTime}, it will be displayed like a normal {@link Ext.field.Text}
 * (but not selectable/changable).
 *
 *     Ext.create('Ext.ux.field.DateTimePicker', {
 *         label: 'Birthday',
 *         value: new Date()
 *     });
 *
 * {@link Ext.ux.field.DateTimePicker} fields are very simple to implement, and have no required configurations.
 *
 * ## Examples
 *
 * It can be very useful to set a default {@link #value} configuration on {@link Ext.ux.field.DateTimePicker} fields. In
 * this example, we set the {@link #value} to be the current date. You can also use the {@link #setValue} method to
 * update the value at any time.
 *
 *     @example miniphone preview
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 items: [
 *                     {
 *                         xtype: 'datetimepickerfield',
 *                         label: 'Birthday',
 *                         name: 'birthday',
 *                         value: new Date()
 *                     }
 *                 ]
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'bottom',
 *                 items: [
 *                     { xtype: 'spacer' },
 *                     {
 *                         text: 'setValue',
 *                         handler: function() {
 *                             var datetimepickerfield = Ext.ComponentQuery.query('datetimepickerfield')[0];
 *
 *                             var randomNumber = function(from, to) {
 *                                 return Math.floor(Math.random() * (to - from + 1) + from);
 *                             };
 *
 *                             datetimepickerfield.setValue({
 *                                 month: randomNumber(0, 11),
 *                                 day  : randomNumber(0, 28),
 *                                 year : randomNumber(1980, 2011)
 *                             });
 *                         }
 *                     },
 *                     { xtype: 'spacer' }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * When you need to retrieve the date from the {@link Ext.ux.field.DateTimePicker}, you can either use the {@link #getValue} or
 * {@link #getFormattedValue} methods:
 *
 *     @example preview
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 items: [
 *                     {
 *                         xtype: 'datetimepickerfield',
 *                         label: 'Birthday',
 *                         name: 'birthday',
 *                         value: new Date()
 *                     }
 *                 ]
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'bottom',
 *                 items: [
 *                     {
 *                         text: 'getValue',
 *                         handler: function() {
 *                             var datetimepickerfield = Ext.ComponentQuery.query('datetimepickerfield')[0];
 *                             Ext.Msg.alert(null, datetimepickerfield.getValue());
 *                         }
 *                     },
 *                     { xtype: 'spacer' },
 *                     {
 *                         text: 'getFormattedValue',
 *                         handler: function() {
 *                             var datetimepickerfield = Ext.ComponentQuery.query('datetimepickerfield')[0];
 *                             Ext.Msg.alert(null, datetimepickerfield.getFormattedValue());
 *                         }
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 *
 */

Ext.define('Ext.ux.field.DateTimePicker', {
    extend: 'Ext.field.Text',
    alternateClassName: 'Ext.form.DateTimePicker',
    xtype: 'datetimepickerfield',
    requires: [
        'Ext.ux.picker.DateTime',
        'Ext.DateExtras'
    ],

    /**
     * @event change
     * Fires when a date is selected
     * @param {Ext.ux.field.DateTimePicker} this
     * @param {Date} date The new date
     */

    config: {
        ui: 'select',

        /**
         * @cfg {Object/Ext.ux.picker.DateTime} picker
         * An object that is used when creating the internal {@link Ext.ux.picker.DateTime} component or a direct instance of {@link Ext.ux.picker.DateTime}
         * Defaults to true
         * @accessor
         */
        picker: true,

        /**
         * @cfg {Boolean}
         * @hide
         * @accessor
         */
        clearIcon: false,

        /**
         * @cfg {Object/Date} value
         * Default value for the field and the internal {@link Ext.ux.picker.DateTime} component. Accepts an object of 'year',
         * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
         *
         * Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
         * @accessor
         */

        /**
         * @cfg {Boolean} destroyPickerOnHide
         * Whether or not to destroy the picker widget on hide. This save memory if it's not used frequently,
         * but increase delay time on the next show due to re-instantiation. Defaults to false
         * @accessor
         */
        destroyPickerOnHide: false,

        /**
         * @cfg {String} dateTimeFormat The format to be used when displaying the date in this field.
         * Accepts any valid datetime format. You can view formats over in the {@link Ext.Date} documentation.
         * Defaults to `Ext.util.Format.defaultDateFormat`.
         */
        dateTimeFormat: 'm/d/Y h:i:A',
        /**
         * @cfg {Object}
         * @hide
         */
        component: {
            useMask: true
        }
    },

    initialize: function() {
        this.callParent();

        this.getComponent().on({
            scope: this,

            masktap: 'onMaskTap'
        });

        this.getComponent().input.dom.disabled = true;
    },

    syncEmptyCls: Ext.emptyFn,

    applyValue: function(value) {
        if (!Ext.isDate(value) && !Ext.isObject(value)) {
            value = null;
        }

        if (Ext.isObject(value)) {
            value = new Date(value.year, value.month - 1, value.day,value.hour,value.minute);
        }

        return value;
    },

    updateValue: function(newValue) {
        var picker = this._picker;
        if (picker && picker.isPicker) {
            picker.setValue(newValue);
        }

        // Ext.Date.format expects a Date
        if (newValue !== null) {
            this.getComponent().setValue(Ext.Date.format(newValue, this.getDateTimeFormat() || Ext.util.Format.defaultDateFormat));
        } else {
            this.getComponent().setValue('');
        }

        if (this._picker && this._picker instanceof Ext.ux.picker.DateTime) {
            this._picker.setValue(newValue);
        }
    },

    /**
     * Updates the date format in the field.
     * @private
     */
    updateDateFormat: function(newDateFormat, oldDateFormat) {
        var value = this.getValue();
        if (newDateFormat != oldDateFormat && Ext.isDate(value) && this._picker && this._picker instanceof Ext.ux.picker.DateTime) {
            this.getComponent().setValue(Ext.Date.format(value, newDateFormat || Ext.util.Format.defaultDateFormat));
        }
    },

    /**
     * Returns the {@link Date} value of this field.
     * If you wanted a formated date
     * @return {Date} The date selected
     */
    getValue: function() {
        if (this._picker && this._picker instanceof Ext.ux.picker.DateTime) {
            return this._picker.getValue();
        }

        return this._value;
    },

    /**
     * Returns the value of the field formatted using the specified format. If it is not specified, it will default to
     * {@link #dateFormat} and then {@link Ext.util.Format#defaultDateFormat}.
     * @param {String} format The format to be returned
     * @return {String} The formatted date
     */
    getFormattedValue: function(format) {
        var value = this.getValue();
        console.log(this.getDateTimeFormat(),"format");
        return (Ext.isDate(value)) ? Ext.Date.format(value, format || this.getDateTimeFormat() || Ext.util.Format.defaultDateFormat) : value;
    },

    applyPicker: function(picker, pickerInstance) {
        if (pickerInstance && pickerInstance.isPicker) {
            picker = pickerInstance.setConfig(picker);
        }

        return picker;
    },

    getPicker: function() {
        var picker = this._picker;

        if (picker && !picker.isPicker) {
            picker = Ext.factory(picker, Ext.ux.picker.DateTime);
            picker.on({
                scope: this,

                change: 'onPickerChange',
                hide  : 'onPickerHide'
            });
            picker.setValue(this.getValue());
            Ext.Viewport.add(picker);
            this._picker = picker;
        }

        return picker;
    },

    /**
     * @private
     * Listener to the tap event of the mask element. Shows the internal DatePicker component when the button has been tapped.
     */
    onMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        if (this.getReadOnly()) {
            return false;
        }

        this.getPicker().show();

        return false;
    },

    /**
     * Called when the picker changes its value
     * @param {Ext.ux.picker.DateTime} picker The date picker
     * @param {Object} value The new value from the date picker
     * @private
     */
    onPickerChange: function(picker, value) {
        var me = this;

        me.setValue(value);
        me.fireEvent('change', me, me.getValue());
    },

    /**
     * Destroys the picker when it is hidden, if
     * {@link Ext.ux.field.DateTimePicker#destroyPickerOnHide destroyPickerOnHide} is set to true
     * @private
     */
    onPickerHide: function() {
        var picker = this.getPicker();
        if (this.getDestroyPickerOnHide() && picker) {
            picker.destroy();
            this._picker = true;
        }
    },

    reset: function() {
        this.setValue(this.originalValue);
    },

    // @private
    destroy: function() {
        var picker = this.getPicker();

        if (picker && picker.isPicker) {
            picker.destroy();
        }

        this.callParent(arguments);
    }
    //<deprecated product=touch since=2.0>
}, function() {
    this.override({
        getValue: function(format) {
            if (format) {
                //<debug warn>
                Ext.Logger.deprecate("format argument of the getValue method is deprecated, please use getFormattedValue instead", this);
                //</debug>
                return this.getFormattedValue(format);
            }
            return this.callOverridden();
        }
    });

    /**
     * @method getDatePicker
     * @inheritdoc Ext.ux.field.DateTimePicker#getPicker
     * @deprecated 2.0.0 Please use #getPicker instead
     */
    Ext.deprecateMethod(this, 'getDatePicker', 'getPicker');
    //</deprecated>
});
<commit_msg>make possible cancelling picker w/o applying date (keep null dates nulled)<commit_after>/**
 * @aside guide forms
 *
 * This is a specialized field which shows a {@link Ext.ux.picker.DateTime} when tapped. If it has a predefined value,
 * or a value is selected in the {@link Ext.ux.picker.DateTime}, it will be displayed like a normal {@link Ext.field.Text}
 * (but not selectable/changable).
 *
 *     Ext.create('Ext.ux.field.DateTimePicker', {
 *         label: 'Birthday',
 *         value: new Date()
 *     });
 *
 * {@link Ext.ux.field.DateTimePicker} fields are very simple to implement, and have no required configurations.
 *
 * ## Examples
 *
 * It can be very useful to set a default {@link #value} configuration on {@link Ext.ux.field.DateTimePicker} fields. In
 * this example, we set the {@link #value} to be the current date. You can also use the {@link #setValue} method to
 * update the value at any time.
 *
 *     @example miniphone preview
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 items: [
 *                     {
 *                         xtype: 'datetimepickerfield',
 *                         label: 'Birthday',
 *                         name: 'birthday',
 *                         value: new Date()
 *                     }
 *                 ]
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'bottom',
 *                 items: [
 *                     { xtype: 'spacer' },
 *                     {
 *                         text: 'setValue',
 *                         handler: function() {
 *                             var datetimepickerfield = Ext.ComponentQuery.query('datetimepickerfield')[0];
 *
 *                             var randomNumber = function(from, to) {
 *                                 return Math.floor(Math.random() * (to - from + 1) + from);
 *                             };
 *
 *                             datetimepickerfield.setValue({
 *                                 month: randomNumber(0, 11),
 *                                 day  : randomNumber(0, 28),
 *                                 year : randomNumber(1980, 2011)
 *                             });
 *                         }
 *                     },
 *                     { xtype: 'spacer' }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * When you need to retrieve the date from the {@link Ext.ux.field.DateTimePicker}, you can either use the {@link #getValue} or
 * {@link #getFormattedValue} methods:
 *
 *     @example preview
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 items: [
 *                     {
 *                         xtype: 'datetimepickerfield',
 *                         label: 'Birthday',
 *                         name: 'birthday',
 *                         value: new Date()
 *                     }
 *                 ]
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'bottom',
 *                 items: [
 *                     {
 *                         text: 'getValue',
 *                         handler: function() {
 *                             var datetimepickerfield = Ext.ComponentQuery.query('datetimepickerfield')[0];
 *                             Ext.Msg.alert(null, datetimepickerfield.getValue());
 *                         }
 *                     },
 *                     { xtype: 'spacer' },
 *                     {
 *                         text: 'getFormattedValue',
 *                         handler: function() {
 *                             var datetimepickerfield = Ext.ComponentQuery.query('datetimepickerfield')[0];
 *                             Ext.Msg.alert(null, datetimepickerfield.getFormattedValue());
 *                         }
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 *
 */

Ext.define('Ext.ux.field.DateTimePicker', {
    extend: 'Ext.field.Text',
    alternateClassName: 'Ext.form.DateTimePicker',
    xtype: 'datetimepickerfield',
    requires: [
        'Ext.ux.picker.DateTime',
        'Ext.DateExtras'
    ],

    /**
     * @event change
     * Fires when a date is selected
     * @param {Ext.ux.field.DateTimePicker} this
     * @param {Date} date The new date
     */

    config: {
        ui: 'select',

        /**
         * @cfg {Object/Ext.ux.picker.DateTime} picker
         * An object that is used when creating the internal {@link Ext.ux.picker.DateTime} component or a direct instance of {@link Ext.ux.picker.DateTime}
         * Defaults to true
         * @accessor
         */
        picker: true,

        /**
         * @cfg {Boolean}
         * @hide
         * @accessor
         */
        clearIcon: false,

        /**
         * @cfg {Object/Date} value
         * Default value for the field and the internal {@link Ext.ux.picker.DateTime} component. Accepts an object of 'year',
         * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
         *
         * Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
         * @accessor
         */

        /**
         * @cfg {Boolean} destroyPickerOnHide
         * Whether or not to destroy the picker widget on hide. This save memory if it's not used frequently,
         * but increase delay time on the next show due to re-instantiation. Defaults to false
         * @accessor
         */
        destroyPickerOnHide: false,

        /**
         * @cfg {String} dateTimeFormat The format to be used when displaying the date in this field.
         * Accepts any valid datetime format. You can view formats over in the {@link Ext.Date} documentation.
         * Defaults to `Ext.util.Format.defaultDateFormat`.
         */
        dateTimeFormat: 'm/d/Y h:i:A',
        /**
         * @cfg {Object}
         * @hide
         */
        component: {
            useMask: true
        }
    },

    initialize: function() {
        this.callParent();

        this.getComponent().on({
            scope: this,

            masktap: 'onMaskTap'
        });

        this.getComponent().input.dom.disabled = true;
    },

    syncEmptyCls: Ext.emptyFn,

    applyValue: function(value) {
        if (!Ext.isDate(value) && !Ext.isObject(value)) {
            value = null;
        }

        if (Ext.isObject(value)) {
            value = new Date(value.year, value.month - 1, value.day,value.hour,value.minute);
        }

        return value;
    },

    updateValue: function(newValue) {
        var picker = this._picker;
        if (picker && picker.isPicker) {
            picker.setValue(newValue);
        }

        // Ext.Date.format expects a Date
        if (newValue !== null) {
            this.getComponent().setValue(Ext.Date.format(newValue, this.getDateTimeFormat() || Ext.util.Format.defaultDateFormat));
        } else {
            this.getComponent().setValue('');
        }

        if (this._picker && this._picker instanceof Ext.ux.picker.DateTime) {
            this._picker.setValue(newValue);
        }
    },

    /**
     * Updates the date format in the field.
     * @private
     */
    updateDateFormat: function(newDateFormat, oldDateFormat) {
        var value = this.getValue();
        if (newDateFormat != oldDateFormat && Ext.isDate(value) && this._picker && this._picker instanceof Ext.ux.picker.DateTime) {
            this.getComponent().setValue(Ext.Date.format(value, newDateFormat || Ext.util.Format.defaultDateFormat));
        }
    },

    /**
     * Returns the {@link Date} value of this field.
     * If you wanted a formated date
     * @return {Date} The date selected
     */
    getValue: function() {
        if (this._picker && this._picker instanceof Ext.ux.picker.DateTime) {
            return this._picker.getValue();
        }

        return this._value;
    },

    /**
     * Returns the value of the field formatted using the specified format. If it is not specified, it will default to
     * {@link #dateFormat} and then {@link Ext.util.Format#defaultDateFormat}.
     * @param {String} format The format to be returned
     * @return {String} The formatted date
     */
    getFormattedValue: function(format) {
        var value = this.getValue();
        console.log(this.getDateTimeFormat(),"format");
        return (Ext.isDate(value)) ? Ext.Date.format(value, format || this.getDateTimeFormat() || Ext.util.Format.defaultDateFormat) : value;
    },

    applyPicker: function(picker, pickerInstance) {
        if (pickerInstance && pickerInstance.isPicker) {
            picker = pickerInstance.setConfig(picker);
        }

        return picker;
    },

    getPicker: function() {
        var picker = this._picker;

        if (picker && !picker.isPicker) {
            picker = Ext.factory(picker, Ext.ux.picker.DateTime);
            picker.on({
                scope: this,
                cancel: 'onPickerCancel',
                change: 'onPickerChange',
                hide  : 'onPickerHide'
            });
            picker.setValue(this.getValue());
            Ext.Viewport.add(picker);
            this._picker = picker;
        }

        return picker;
    },

    /**
     * @private
     * Listener to the tap event of the mask element. Shows the internal DatePicker component when the button has been tapped.
     */
    onMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        if (this.getReadOnly()) {
            return false;
        }

        this.getPicker().show();

        return false;
    },

    /**
     * @private
     * Revert internal date so field won't appear changed
     */
    onPickerCancel: function(picker, options) {
        this._picker = this._picker.config;
        picker.destroy();
        return true;
    },

    /**
     * Called when the picker changes its value
     * @param {Ext.ux.picker.DateTime} picker The date picker
     * @param {Object} value The new value from the date picker
     * @private
     */
    onPickerChange: function(picker, value) {
        var me = this;

        me.setValue(value);
        me.fireEvent('change', me, me.getValue());
    },

    /**
     * Destroys the picker when it is hidden, if
     * {@link Ext.ux.field.DateTimePicker#destroyPickerOnHide destroyPickerOnHide} is set to true
     * @private
     */
    onPickerHide: function() {
        var picker = this.getPicker();
        if (this.getDestroyPickerOnHide() && picker) {
            picker.destroy();
            this._picker = true;
        }
    },

    reset: function() {
        this.setValue(this.originalValue);
    },

    // @private
    destroy: function() {
        var picker = this.getPicker();

        if (picker && picker.isPicker) {
            picker.destroy();
        }

        this.callParent(arguments);
    }
    //<deprecated product=touch since=2.0>
}, function() {
    this.override({
        getValue: function(format) {
            if (format) {
                //<debug warn>
                Ext.Logger.deprecate("format argument of the getValue method is deprecated, please use getFormattedValue instead", this);
                //</debug>
                return this.getFormattedValue(format);
            }
            return this.callOverridden();
        }
    });

    /**
     * @method getDatePicker
     * @inheritdoc Ext.ux.field.DateTimePicker#getPicker
     * @deprecated 2.0.0 Please use #getPicker instead
     */
    Ext.deprecateMethod(this, 'getDatePicker', 'getPicker');
    //</deprecated>
});
<|endoftext|>


<commit_before>/* eslint-env es6 */

import _ from 'lodash'
import path from 'path'
import webpack from 'webpack'
import autoprefixer from 'autoprefixer'
import ProgressBar from 'progress'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import S3Plugin from 'webpack-s3-plugin'
import CompressionPlugin from 'compression-webpack-plugin'
import WebpackDashboard from 'webpack-dashboard/plugin'

import {vendor} from './vendors.json'
import {name} from './package.json'

const {
  CommonsChunkPlugin,
  DedupePlugin,
  OccurenceOrderPlugin,
  LimitChunkCountPlugin,
  MinChunkSizePlugin,
  UglifyJsPlugin
} = webpack.optimize

const {DefinePlugin, ProgressPlugin} = webpack,
      CONTEXT = path.resolve(__dirname),
      rootPath = (nPath) => path.resolve(CONTEXT, nPath),
      DEV_SERVER_PORT = 4000,
      APP_ROOT = rootPath('src'),
      PUBLIC_PATH = rootPath('public'),
      NODE_MODULES_PATH = rootPath('node_modules'),
      appPath = (nPath) => path.resolve(APP_ROOT, nPath),
      {NODE_ENV, AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_REGION, AWS_BUCKET, CDN_URL} = process.env,
      BUILD_PATH = rootPath('build'),
      IS_WEBWORKER = false

const progressBar = new ProgressBar('[:bar] :percent (:current/:total)', {
  complete: '-',
  incomplete: ' ',
  total: 100
})

var devtool

const HTML_PLUGIN_CONFIG = {
  inject: false,
  title: name,
  chunksSortMode: 'dependency',
  minify: IS_BUILD ? {caseSensitive: true} : false,
  template: `pug!${appPath('index.pug')}`,
  favicon: path.resolve(__dirname, 'favicon.ico'),
}

// use when https://github.com/jantimon/favicons-webpack-plugin/issues/29 is fixed
// FaviconsWebpackPlugin(FAVICON_CONFIG),
const FAVICON_CONFIG = {
  logo: 'favicon.png',
  title: name,
  inject: false
}

const ENTRY = {
  vendor,
  app: './src/main.ts',
  styles: './src/style/global.scss'
}

const ENV = {
  __DEV__: NODE_ENV === 'development',
  __PROD__: NODE_ENV === 'production',
  __TEST__: NODE_ENV === 'test',
  __STAGING__: NODE_ENV === 'staging',
  __IS_WEBWORKER__: IS_WEBWORKER
}

const IS_BUILD = ENV.__STAGING__ || ENV.__PROD__,
      DEFAULT_CDN = CDN_URL || `https://s3-us-west-2.amazonaws.com/${AWS_BUCKET}`,
      SASS_LOADER = `${IS_BUILD ? 'postcss!' : ''}sass?sourceMap`

Object.assign(ENV, {__IS_BUILD__: IS_BUILD})

if (ENV.__IS_WEBWORKER__) {
  Object.assign(ENTRY, {webworker: './src/main.webworker.ts'})
  Object.assign(HTML_PLUGIN_CONFIG, {
    excludeChunks: ['app']
  })
}

var preLoaders = {
  tslint: {
    test: /\.ts$/,
    loader: 'tslint',
    exclude: [NODE_MODULES_PATH],
    include: [APP_ROOT]
  },

  systemJS: {
    test: /\.ts$/,
    loader: 'string-replace',
    query: {
      search: '(System|SystemJS)(.*[\\n\\r]\\s*\\.|\\.)import\\((.+)\\)',
      replace: '$1.import($3).then(mod => mod.__esModule ? mod.default : mod)',
      flags: 'g'
    },
    include: [appPath('app'), appPath('shared')]
  }
}

var loaders = {
  javascript: {
    test: /\.ts$/,
    loader: ['babel', 'ts', 'angular2-template'].concat(IS_BUILD ? [] : '@angularclass/hmr-loader'),
    exclude: [NODE_MODULES_PATH],
    include: [APP_ROOT]
  },

  html: {
    test: /\.pug/,
    loader: ['apply', 'pug'],
    include: [appPath('app'), appPath('shared')]
  },

  globalCss: {
    test: /\.s?css$/,
    loader: ENV.__TEST__ ? 'null' : `style!css?sourceMap!${SASS_LOADER}`,
    include: [appPath('style')]
  },

  // For to-string removes the ability to cache css so we use raw in development
  componentCss: {
    test: /\.s?css$/,
    loader: ENV.__TEST__ ? 'null' : `raw!${SASS_LOADER}`,
    exclude: [appPath('style')]
  },

  json: {
    test: /\.json$/,
    loader: 'json'
  },

  file: {
    test: /\.(png|gif|jpg|jpeg|woff|woff2|eof|ttf)$/,
    loader: `file${IS_BUILD ? '?name=[name]-[hash].[ext]' : ''}!image-webpack?bypassOnDebug`,
    include: [rootPath('public/img')]
  },

  svg: {
    test: /\.svg$/,
    loader: 'image-webpack?bypassOnDebug!svg-inline',
    include: [rootPath('public/svg')]
  }
}

if (ENV.__PROD__)
  devtool = false
else if (ENV.__DEV__)
  devtool = '#source-map'
else if (ENV.__STAGING__ || ENV.__TEST__)
  devtool = '#inline-source-map'

var config = {
  context: CONTEXT,
  devtool,
  debug: !ENV.__PROD__ && !ENV.__STAGING__,
  entry: ENTRY,

  output: {
    path: BUILD_PATH,
    publicPath: IS_BUILD ? DEFAULT_CDN : '',
    filename: IS_BUILD ? '[name]-[chunkhash].js' : '[name].js',
    sourceMapFilename: '[name].map',
    chunkFilename: IS_BUILD ? '[id].chunk-[chunkhash].js': '[id].chunk.js'
  },

  resolve: {
    cache: ENV.__TEST__,
    extensions: ['', '.ts', '.js', '.json'],
    modules: [NODE_MODULES_PATH, PUBLIC_PATH, APP_ROOT],
    alias: {
      vendor: rootPath('vendor')
    }
  },

  module: {
    noParse: [/.+zone\.js\/dist\/.+/, /.+angular2\/bundles\/.+/],
    preLoaders: Object.values(preLoaders),
    loaders: Object.values(loaders)
  },

  plugins: [
    new DefinePlugin(ENV),
    new HtmlWebpackPlugin(HTML_PLUGIN_CONFIG),
    new ProgressPlugin(percentage => progressBar.update(percentage))
  ],

  tslint: {
    emitErrors: false,
    failOnHint: false
  },

  postcss() {
    return [autoprefixer]
  },

  devServer: {
    stats: 'minimal',
    port: DEV_SERVER_PORT,
    // Sample Proxy Config
    //proxy: [{
      //path: '/api/*',
      //target: 'http://localhost:4000'
    //}],

    historyApiFallback: true,
    // {
    //   // If you have multiple entrypoints add them here
    //   rewrites: [{
    //     from: /.*/,
    //     to: '/index.html'
    //   }]
    // },

    watchOptions: {
      aggregateTimeout: 300
    }
  }
}

if (!ENV.__TEST__)
  config.plugins.push(
    new CommonsChunkPlugin({
      name: 'vendor',
      chunks: Object.keys(ENTRY).filter(key => ENV.__IS_WEBWORKER__ ? key !== 'app' : true),
      filename: IS_BUILD ? 'vendor-[chunkhash].js' : 'vendor.js',
      minChunks: Infinity
    })
  )

if (ENV.__DEV__) {
  var WebpackNotifierPlugin = require('webpack-notifier')

  config.plugins.push(
    // new WebpackDashboard({port: DEV_SERVER_PORT, title: name}),
    new WebpackNotifierPlugin({
      title: name,
      contentImage: rootPath('./favicon.ico')
    })
  )
} else if (IS_BUILD) {
  loaders.globalCss.loader = ExtractTextPlugin.extract(loaders.globalCss.loader.replace('style!', ''))

  config.plugins.push(
    // https://github.com/webpack/webpack/issues/2644
    // new DedupePlugin(),
    new ExtractTextPlugin('[name]-[chunkhash].css'),
    new LimitChunkCountPlugin({maxChunks: 15}),
    new MinChunkSizePlugin({minChunkSize: 10000}),
    new UglifyJsPlugin()
    new CommonsChunkPlugin({
      name: 'common',
      async: true,
      minChunks: 2
    })
  )
} else if (ENV.__TEST__) {
  config.stats = {
    colors: true,
    reasons: true
  }

  config.node = _.extend(config.node, {
    global: 'window',
    progress: false,
    crypto: 'empty',
    module: false,
    clearImmediate: false,
    setImmediate: false
  })

  config.module.noParse = [
    /zone\.js\/dist\/zone-microtask\.js/,
    /zone\.js\/dist\/long-stack-trace-zone\.js/,
    /zone\.js\/dist\/jasmine-patch\.js/
  ]
}

if (ENV.__PROD__)
  config.plugins.push(
    new CompressionPlugin({
      asset: '[path]',
      algorithm: 'gzip',
      minRatio: 0.8
    }),

    new S3Plugin({
      exclude: /.*\.html$/,
      s3Options: {
        accessKeyId: AWS_ACCESS_KEY,
        secretAccessKey: AWS_SECRET_KEY,
        region: AWS_REGION
      },
      s3UploadOptions: {
        Bucket: AWS_BUCKET,
        CacheControl: 'max-age=315360000, no-transform, public',
        ContentEncoding: 'gzip'
      },
      cdnizerOptions: {
        defaultCDNBase: DEFAULT_CDN
      }
    }),

    new S3Plugin({
      directory: BUILD_PATH,
      basePath: 'public/',
      exclude: /\.svg$/,
      include: loaders.file.test,
      s3Options: {
        accessKeyId: AWS_ACCESS_KEY,
        secretAccessKey: AWS_SECRET_KEY,
        region: AWS_REGION
      },
      s3UploadOptions: {
        Bucket: AWS_BUCKET,
        CacheControl: 'max-age=315360000, no-transform, public'
      }
    })
  )
else
  vendor.push('zone.js/dist/long-stack-trace-zone')

export default config
<commit_msg>Comment out upload of public<commit_after>/* eslint-env es6 */

import _ from 'lodash'
import path from 'path'
import webpack from 'webpack'
import autoprefixer from 'autoprefixer'
import ProgressBar from 'progress'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import S3Plugin from 'webpack-s3-plugin'
import CompressionPlugin from 'compression-webpack-plugin'
import WebpackDashboard from 'webpack-dashboard/plugin'

import {vendor} from './vendors.json'
import {name} from './package.json'

const {
  CommonsChunkPlugin,
  DedupePlugin,
  OccurenceOrderPlugin,
  LimitChunkCountPlugin,
  MinChunkSizePlugin,
  UglifyJsPlugin
} = webpack.optimize

const {DefinePlugin, ProgressPlugin} = webpack,
      CONTEXT = path.resolve(__dirname),
      rootPath = (nPath) => path.resolve(CONTEXT, nPath),
      DEV_SERVER_PORT = 4000,
      APP_ROOT = rootPath('src'),
      PUBLIC_PATH = rootPath('public'),
      NODE_MODULES_PATH = rootPath('node_modules'),
      appPath = (nPath) => path.resolve(APP_ROOT, nPath),
      {NODE_ENV, AWS_ACCESS_KEY, AWS_SECRET_KEY, AWS_REGION, AWS_BUCKET, CDN_URL} = process.env,
      BUILD_PATH = rootPath('build'),
      IS_WEBWORKER = false

const progressBar = new ProgressBar('[:bar] :percent (:current/:total)', {
  complete: '-',
  incomplete: ' ',
  total: 100
})

var devtool

const HTML_PLUGIN_CONFIG = {
  inject: false,
  title: name,
  chunksSortMode: 'dependency',
  minify: IS_BUILD ? {caseSensitive: true} : false,
  template: `pug!${appPath('index.pug')}`,
  favicon: path.resolve(__dirname, 'favicon.ico'),
}

// use when https://github.com/jantimon/favicons-webpack-plugin/issues/29 is fixed
// FaviconsWebpackPlugin(FAVICON_CONFIG),
const FAVICON_CONFIG = {
  logo: 'favicon.png',
  title: name,
  inject: false
}

const ENTRY = {
  vendor,
  app: './src/main.ts',
  styles: './src/style/global.scss'
}

const ENV = {
  __DEV__: NODE_ENV === 'development',
  __PROD__: NODE_ENV === 'production',
  __TEST__: NODE_ENV === 'test',
  __STAGING__: NODE_ENV === 'staging',
  __IS_WEBWORKER__: IS_WEBWORKER
}

const IS_BUILD = ENV.__STAGING__ || ENV.__PROD__,
      DEFAULT_CDN = CDN_URL || `https://s3-us-west-2.amazonaws.com/${AWS_BUCKET}`,
      SASS_LOADER = `${IS_BUILD ? 'postcss!' : ''}sass?sourceMap`

Object.assign(ENV, {__IS_BUILD__: IS_BUILD})

if (ENV.__IS_WEBWORKER__) {
  Object.assign(ENTRY, {webworker: './src/main.webworker.ts'})
  Object.assign(HTML_PLUGIN_CONFIG, {
    excludeChunks: ['app']
  })
}

var preLoaders = {
  tslint: {
    test: /\.ts$/,
    loader: 'tslint',
    exclude: [NODE_MODULES_PATH],
    include: [APP_ROOT]
  },

  systemJS: {
    test: /\.ts$/,
    loader: 'string-replace',
    query: {
      search: '(System|SystemJS)(.*[\\n\\r]\\s*\\.|\\.)import\\((.+)\\)',
      replace: '$1.import($3).then(mod => mod.__esModule ? mod.default : mod)',
      flags: 'g'
    },
    include: [appPath('app'), appPath('shared')]
  }
}

var loaders = {
  javascript: {
    test: /\.ts$/,
    loader: ['babel', 'ts', 'angular2-template'].concat(IS_BUILD ? [] : '@angularclass/hmr-loader'),
    exclude: [NODE_MODULES_PATH],
    include: [APP_ROOT]
  },

  html: {
    test: /\.pug/,
    loader: ['apply', 'pug'],
    include: [appPath('app'), appPath('shared')]
  },

  globalCss: {
    test: /\.s?css$/,
    loader: ENV.__TEST__ ? 'null' : `style!css?sourceMap!${SASS_LOADER}`,
    include: [appPath('style')]
  },

  // For to-string removes the ability to cache css so we use raw in development
  componentCss: {
    test: /\.s?css$/,
    loader: ENV.__TEST__ ? 'null' : `raw!${SASS_LOADER}`,
    exclude: [appPath('style')]
  },

  json: {
    test: /\.json$/,
    loader: 'json'
  },

  file: {
    test: /\.(png|gif|jpg|jpeg|woff|woff2|eof|ttf)$/,
    loader: `file${IS_BUILD ? '?name=[name]-[hash].[ext]' : ''}!image-webpack?bypassOnDebug`,
    include: [rootPath('public/img')]
  },

  svg: {
    test: /\.svg$/,
    loader: 'image-webpack?bypassOnDebug!svg-inline',
    include: [rootPath('public/svg')]
  }
}

if (ENV.__PROD__)
  devtool = false
else if (ENV.__DEV__)
  devtool = '#source-map'
else if (ENV.__STAGING__ || ENV.__TEST__)
  devtool = '#inline-source-map'

var config = {
  context: CONTEXT,
  devtool,
  debug: !ENV.__PROD__ && !ENV.__STAGING__,
  entry: ENTRY,

  output: {
    path: BUILD_PATH,
    publicPath: IS_BUILD ? DEFAULT_CDN : '',
    filename: IS_BUILD ? '[name]-[chunkhash].js' : '[name].js',
    sourceMapFilename: '[name].map',
    chunkFilename: IS_BUILD ? '[id].chunk-[chunkhash].js': '[id].chunk.js'
  },

  resolve: {
    cache: ENV.__TEST__,
    extensions: ['', '.ts', '.js', '.json'],
    modules: [NODE_MODULES_PATH, PUBLIC_PATH, APP_ROOT],
    alias: {
      vendor: rootPath('vendor')
    }
  },

  module: {
    noParse: [/.+zone\.js\/dist\/.+/, /.+angular2\/bundles\/.+/],
    preLoaders: Object.values(preLoaders),
    loaders: Object.values(loaders)
  },

  plugins: [
    new DefinePlugin(ENV),
    new HtmlWebpackPlugin(HTML_PLUGIN_CONFIG),
    new ProgressPlugin(percentage => progressBar.update(percentage))
  ],

  tslint: {
    emitErrors: false,
    failOnHint: false
  },

  postcss() {
    return [autoprefixer]
  },

  devServer: {
    stats: 'minimal',
    port: DEV_SERVER_PORT,
    // Sample Proxy Config
    //proxy: [{
      //path: '/api/*',
      //target: 'http://localhost:4000'
    //}],

    historyApiFallback: true,
    // {
    //   // If you have multiple entrypoints add them here
    //   rewrites: [{
    //     from: /.*/,
    //     to: '/index.html'
    //   }]
    // },

    watchOptions: {
      aggregateTimeout: 300
    }
  }
}

if (!ENV.__TEST__)
  config.plugins.push(
    new CommonsChunkPlugin({
      name: 'vendor',
      chunks: Object.keys(ENTRY).filter(key => ENV.__IS_WEBWORKER__ ? key !== 'app' : true),
      filename: IS_BUILD ? 'vendor-[chunkhash].js' : 'vendor.js',
      minChunks: Infinity
    })
  )

if (ENV.__DEV__) {
  var WebpackNotifierPlugin = require('webpack-notifier')

  config.plugins.push(
    // new WebpackDashboard({port: DEV_SERVER_PORT, title: name}),
    new WebpackNotifierPlugin({
      title: name,
      contentImage: rootPath('./favicon.ico')
    })
  )
} else if (IS_BUILD) {
  loaders.globalCss.loader = ExtractTextPlugin.extract(loaders.globalCss.loader.replace('style!', ''))

  config.plugins.push(
    // https://github.com/webpack/webpack/issues/2644
    // new DedupePlugin(),
    new ExtractTextPlugin('[name]-[chunkhash].css'),
    new LimitChunkCountPlugin({maxChunks: 15}),
    new MinChunkSizePlugin({minChunkSize: 10000}),
    new UglifyJsPlugin()
    new CommonsChunkPlugin({
      name: 'common',
      async: true,
      minChunks: 2
    })
  )
} else if (ENV.__TEST__) {
  config.stats = {
    colors: true,
    reasons: true
  }

  config.node = _.extend(config.node, {
    global: 'window',
    progress: false,
    crypto: 'empty',
    module: false,
    clearImmediate: false,
    setImmediate: false
  })

  config.module.noParse = [
    /zone\.js\/dist\/zone-microtask\.js/,
    /zone\.js\/dist\/long-stack-trace-zone\.js/,
    /zone\.js\/dist\/jasmine-patch\.js/
  ]
}

if (ENV.__PROD__)
  config.plugins.push(
    new CompressionPlugin({
      asset: '[path]',
      algorithm: 'gzip',
      minRatio: 0.8
    }),

    new S3Plugin({
      exclude: /.*\.html$/,
      s3Options: {
        accessKeyId: AWS_ACCESS_KEY,
        secretAccessKey: AWS_SECRET_KEY,
        region: AWS_REGION
      },
      s3UploadOptions: {
        Bucket: AWS_BUCKET,
        CacheControl: 'max-age=315360000, no-transform, public',
        ContentEncoding: 'gzip'
      },
      cdnizerOptions: {
        defaultCDNBase: DEFAULT_CDN
      }
    })

    // new S3Plugin({
    //   directory: BUILD_PATH,
    //   basePath: 'public/',
    //   exclude: /\.svg$/,
    //   include: loaders.file.test,
    //   s3Options: {
    //     accessKeyId: AWS_ACCESS_KEY,
    //     secretAccessKey: AWS_SECRET_KEY,
    //     region: AWS_REGION
    //   },
    //   s3UploadOptions: {
    //     Bucket: AWS_BUCKET,
    //     CacheControl: 'max-age=315360000, no-transform, public'
    //   }
    // })
  )
else
  vendor.push('zone.js/dist/long-stack-trace-zone')

export default config
<|endoftext|>


<commit_before>export default class Alphanumeric {
  constructor(lang = 'en') {
    this.lang = lang;
  }

  randomNumber(min = 0, max = 1000) {
    return Math.floor(getRandomNumber(min, max));
  }

  randomFloat(min = 0, max = 1000, precision = 5) {
    return (getRandomNumber(min, max)).toPrecision(precision);
  }

  listOfRandomNumbers(length = 100, min = 0, max = 1000) {
    return new Array(length).fill(this.randomNumber(min, max));
  }
}

function getRandomNumber(min, max) {
  return Math.random() * (max - min + 1) + min;
}
<commit_msg>move method in alpha.js before export to make eslint happy<commit_after>function getRandomNumber(min, max) {
  return Math.random() * (max - min + 1) + min;
}

export default class Alphanumeric {
  constructor(lang = 'en') {
    this.lang = lang;
  }

  randomNumber(min = 0, max = 1000) {
    return Math.floor(getRandomNumber(min, max));
  }

  randomFloat(min = 0, max = 1000, precision = 5) {
    return (getRandomNumber(min, max)).toPrecision(precision);
  }

  listOfRandomNumbers(length = 100, min = 0, max = 1000) {
    return new Array(length).fill(this.randomNumber(min, max));
  }
}
<|endoftext|>


<commit_before>/*
Copyright (C) 2015, MuChu Hsu
Contributed by Muchu Hsu (muchu1983@gmail.com)
This file is part of BSD license

<https://opensource.org/licenses/BSD-3-Clause>
*/
(function($){
    var strApiServerDomain = "http://bennu.ddns.net:5000"; //deploy
    //var strApiServerDomain = "http://127.0.0.1:5000"; //test
    var intCurrentStoryId;

    /* 呼叫 story_chain JSONP API */
    function callJsonpApi(strApiUrl, dicJsonData, funcCallback){
        $.ajax({url: strApiUrl,
            data: dicJsonData,
            success: funcCallback,
            dataType: "jsonp",
            jsonp: "strJsonpCallback",
            crossDomain: true
        });
    };

    /* html document ready */
    $(document).ready(function(){
        /* 新的故事 */
        $("#btn_create_first_story").click(function(){ //按下 新的故事
            intCurrentStoryId = 0;
            $("#textarea_new_story_content").jqteVal(""); //清空 editor
        });
        /* 編輯故事 完成 */
        $("#btn_edit_ok").click(function(){
            var intPrevStoryId = intCurrentStoryId;
            var strStoryContent = $("#textarea_new_story_content").val();
            // ajax api /story_chain/api_post/story
            var strApiUrl = strApiServerDomain + "/story_chain/api_post/story";
            var dicJsonData = {int_prev_story_id : intPrevStoryId,
                             str_story_content : strStoryContent};
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                intCurrentStoryId = response.new_story_id;
                //彈出確認 popup
                $("#popup_story_create_okay").popup("open");
            });
        });
        /* 確認 段落已新增 */
        $("#popup_story_create_okay #btn_okay").click(function(){
            // 切換至閱讀頁
            $("body").pagecontainer("change", "#read_story_page", {});
        });
        /* 編輯故事 取消 */
        $("#btn_edit_cancel").click(function(){
            if (intCurrentStoryId == 0){
                // 切換至首頁
                $("body").pagecontainer("change", "#start_page", {});
            }else{
                // 切換至閱讀頁
                $("body").pagecontainer("change", "#read_story_page", {});
            };
        });
        /* 切換頁面 */
        $(document).on("pagecontainerchange", function(){
            var pageId = $("body").pagecontainer("getActivePage").prop("id");
            switch(pageId){
                case "read_story_page":
                    /* 切換至閱讀頁 */
                    // ajax api /story_chain/api_get/story/<int:intStoryId>
                    var strApiUrl = strApiServerDomain + "/story_chain/api_get/story/" + intCurrentStoryId;
                    callJsonpApi(strApiUrl, {}, function(response){
                        $("span#like_count").text(response.int_like);
                        $("span#dislike_count").text(response.int_dislike);
                        $("#current_story_content").html(response.str_content);
                        $("#current_story_id").html("ID: " + intCurrentStoryId);
                    });
                    break;
                case "select_story_page":
                    /*切換至段落選擇頁*/
                    break;
                default:
                    break;
            };
        });
        /*前一段*/
        $("#btn_prev_story").click(function(){
            // ajax api /story_chain/api_get/story
            var strApiUrl = strApiServerDomain + "/story_chain/api_get/story";
            var dicJsonData = {str_type: "prev", int_story_id: intCurrentStoryId};
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                if(response.int_prev_story_id == 0){
                    // 已是故事的開頭
                    $("#popup_prev_story_not_exists").popup("open");
                }else{
                    // 已取得前一段落 id
                    intCurrentStoryId = response.int_prev_story_id;
                    // 切換至閱讀頁
                    $("body").pagecontainer("change", "#read_story_page", {});
                };
            });
        });
        /*接寫新段落*/
        $("#btn_create_next_story").click(function(){
            // 清空 editor
            $("#textarea_new_story_content").jqteVal("");
        });
        /*下一段*/
        $("#btn_next_story").click(function(){
            // ajax api /story_chain/api_get/story
            var strApiUrl = strApiServerDomain + "/story_chain/api_get/story";
            var dicJsonData = {str_type: "next", int_story_id: intCurrentStoryId};
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                var lst_int_next_story_id = response.lst_int_next_story_id;
                if(lst_int_next_story_id.length == 0){
                    // 已沒有下一段故事
                    $("#popup_next_story_not_exists").popup("open");
                }else{
                    $("ul#ul_story_option_list").html(""); //empty ul
                    for (i = 0; i < lst_int_next_story_id.length; i++) {
                        var intNextStoryId = lst_int_next_story_id[i];
                        var strOptionHtml = ["
",
                                                "",
                                                        "ID: " + intNextStoryId,
                                                "",
                                             "
"].join("");
                        $("ul#ul_story_option_list").append(strOptionHtml);
                    };
                    // 切換至段落選擇頁
                    $("body").pagecontainer("change", "#select_story_page", {});
                };
            });
        });
        /*選擇了某段落選項*/
        $(".story_option").click(function(){
            alert("option selected.")
        });
        /*舊的回憶*/
        /*範例*/
        var strApiUrl = strApiServerDomain + "/jsonpapi";
        var dicJsonData = {x:"555", y:"333"};
        $("#jsonpapi_result").click(function(){
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                $("#jsonpapi_result").text(response.result);
            });
        });
    });

})(jQuery);<commit_msg>option unbind click<commit_after>/*
Copyright (C) 2015, MuChu Hsu
Contributed by Muchu Hsu (muchu1983@gmail.com)
This file is part of BSD license

<https://opensource.org/licenses/BSD-3-Clause>
*/
(function($){
    var strApiServerDomain = "http://bennu.ddns.net:5000"; //deploy
    //var strApiServerDomain = "http://127.0.0.1:5000"; //test
    var intCurrentStoryId;

    /* 呼叫 story_chain JSONP API */
    function callJsonpApi(strApiUrl, dicJsonData, funcCallback){
        $.ajax({url: strApiUrl,
            data: dicJsonData,
            success: funcCallback,
            dataType: "jsonp",
            jsonp: "strJsonpCallback",
            crossDomain: true
        });
    };

    /* html document ready */
    $(document).ready(function(){
        /* 新的故事 */
        $("#btn_create_first_story").click(function(){ //按下 新的故事
            intCurrentStoryId = 0;
            $("#textarea_new_story_content").jqteVal(""); //清空 editor
        });
        /* 編輯故事 完成 */
        $("#btn_edit_ok").click(function(){
            var intPrevStoryId = intCurrentStoryId;
            var strStoryContent = $("#textarea_new_story_content").val();
            // ajax api /story_chain/api_post/story
            var strApiUrl = strApiServerDomain + "/story_chain/api_post/story";
            var dicJsonData = {int_prev_story_id : intPrevStoryId,
                             str_story_content : strStoryContent};
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                intCurrentStoryId = response.new_story_id;
                //彈出確認 popup
                $("#popup_story_create_okay").popup("open");
            });
        });
        /* 確認 段落已新增 */
        $("#popup_story_create_okay #btn_okay").click(function(){
            // 切換至閱讀頁
            $("body").pagecontainer("change", "#read_story_page", {});
        });
        /* 編輯故事 取消 */
        $("#btn_edit_cancel").click(function(){
            if (intCurrentStoryId == 0){
                // 切換至首頁
                $("body").pagecontainer("change", "#start_page", {});
            }else{
                // 切換至閱讀頁
                $("body").pagecontainer("change", "#read_story_page", {});
            };
        });
        /* 切換頁面 */
        $(document).on("pagecontainerchange", function(){
            var pageId = $("body").pagecontainer("getActivePage").prop("id");
            switch(pageId){
                case "read_story_page":
                    /* 切換至閱讀頁 */
                    // ajax api /story_chain/api_get/story/<int:intStoryId>
                    var strApiUrl = strApiServerDomain + "/story_chain/api_get/story/" + intCurrentStoryId;
                    callJsonpApi(strApiUrl, {}, function(response){
                        $("span#like_count").text(response.int_like);
                        $("span#dislike_count").text(response.int_dislike);
                        $("#current_story_content").html(response.str_content);
                        $("#current_story_id").html("ID: " + intCurrentStoryId);
                    });
                    break;
                case "select_story_page":
                    /*切換至段落選擇頁*/
                    break;
                default:
                    break;
            };
        });
        /*前一段*/
        $("#btn_prev_story").click(function(){
            // ajax api /story_chain/api_get/story
            var strApiUrl = strApiServerDomain + "/story_chain/api_get/story";
            var dicJsonData = {str_type: "prev", int_story_id: intCurrentStoryId};
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                if(response.int_prev_story_id == 0){
                    // 已是故事的開頭
                    $("#popup_prev_story_not_exists").popup("open");
                }else{
                    // 已取得前一段落 id
                    intCurrentStoryId = response.int_prev_story_id;
                    // 切換至閱讀頁
                    $("body").pagecontainer("change", "#read_story_page", {});
                };
            });
        });
        /*接寫新段落*/
        $("#btn_create_next_story").click(function(){
            // 清空 editor
            $("#textarea_new_story_content").jqteVal("");
        });
        /*下一段*/
        $("#btn_next_story").click(function(){
            // ajax api /story_chain/api_get/story
            var strApiUrl = strApiServerDomain + "/story_chain/api_get/story";
            var dicJsonData = {str_type: "next", int_story_id: intCurrentStoryId};
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                var lst_int_next_story_id = response.lst_int_next_story_id;
                if(lst_int_next_story_id.length == 0){
                    // 已沒有下一段故事
                    $("#popup_next_story_not_exists").popup("open");
                }else{
                    $("ul#ul_story_option_list").html(""); //empty ul
                    for (i = 0; i < lst_int_next_story_id.length; i++) {
                        var intNextStoryId = lst_int_next_story_id[i];
                        var strOptionHtml = ["
",
                                                "",
                                                        "ID: " + intNextStoryId,
                                                "",
                                             "
"].join("");
                        $("ul#ul_story_option_list").append(strOptionHtml);
                    };
                    /*選擇了某段落選項*/
                    // bind event
                    $(".story_option").click(function(){
                        var intSelectedStoryId = parseInt($(this).attr("value"));
                        intCurrentStoryId = intSelectedStoryId;
                        // unbind event
                        $(".story_option").unbind("click");
                    });
                    // 切換至段落選擇頁
                    $("body").pagecontainer("change", "#select_story_page", {});
                };
            });
        });

        /*舊的回憶*/
        /*範例*/
        var strApiUrl = strApiServerDomain + "/jsonpapi";
        var dicJsonData = {x:"555", y:"333"};
        $("#jsonpapi_result").click(function(){
            callJsonpApi(strApiUrl, dicJsonData, function(response){
                $("#jsonpapi_result").text(response.result);
            });
        });
    });

})(jQuery);<|endoftext|>


<commit_before>define(
	[
		"./meta"
	],
	function (meta) {
		"use strict";

		meta({
			"entity": "module",
			"export": "Array",
			"description": "Augments global Array class"
		});

		meta({
			"entity": "method",
			"for": "Array",
			"name": "fromArguments",
			"static": true,
			"description": "Gets a real array from an arguments object.",
			"arguments": [{
				"name": "args",
				"type": "arguments",
				"description": "An arguments object from within a function"
			}],
			"return": {
				"type": "array"
			}
		});

		meta({
			"name": "forEach",
			"entity": "method",
			"for": "Array",
			"polyfill": true,
			"description": "Executes a provided function once per array element.",
			"arguments": [{
				"name": "callback",
				"type": "function",
				"description": "Function to execute for each element."
			}, {
				"name": "context",
				"type": "object",
				"description": "Object to use as this when executing callback.",
				"required": false
			}]
		});

		meta({
			"entity": "method",
			"for": "Array",
			"name": "map",
			"polyfill": true,
			"description": "Creates a new array with the results of calling a provided function on every element in this array.",
			"arguments": [{
				"name": "callback",
				"type": "function",
				"description": "Function that produces an element of the new Array from an element of the current one."
			}, {
				"name": "context",
				"type": "object",
				"description": "Object to use as this when executing callback."
			}],
			"return": {
				"type": "array"
			}
		});

		var slice = Array.prototype.slice;

		Array.fromArguments = function (args) {
			return slice.call(args, 0);
		};

		if (!Array.prototype.forEach) {
			Array.prototype.forEach = function(callback, context) {
				var i = 0,
					len = this.length;

				for (; i < len; ++i) {
					callback.call(context, this[i], i, this);
				}
			};
		}

		if (!Array.prototype.map) {
			Array.prototype.map = function (callback, context) {
				var mapped = [];
				this.forEach(map);
				function map (element, index, array) {
					mapped[index] = callback.call(context, element, index, array);
				}
				return mapped;
			};
		}
	}
);
<commit_msg>adding filter to Array shim<commit_after>define(
	[
		"./meta"
	],
	function (meta) {
		"use strict";

		meta({
			"entity": "module",
			"export": "Array",
			"description": "Augments global Array class"
		});

		meta({
			"entity": "method",
			"for": "Array",
			"name": "fromArguments",
			"static": true,
			"description": "Gets a real array from an arguments object.",
			"arguments": [{
				"name": "args",
				"type": "arguments",
				"description": "An arguments object from within a function"
			}],
			"return": "array"
		});

		meta({
			"name": "forEach",
			"entity": "method",
			"for": "Array",
			"polyfill": true,
			"description": "Executes a provided function once per array element.",
			"arguments": [{
				"name": "callback",
				"type": "function",
				"description": "Function to execute for each element."
			}, {
				"name": "context",
				"type": "object",
				"description": "Object to use as this when executing callback.",
				"required": false
			}]
		});

		meta({
			"entity": "method",
			"for": "Array",
			"name": "map",
			"polyfill": true,
			"description": "Creates a new array with the results of calling a provided function on every element in this array.",
			"arguments": [{
				"name": "callback",
				"type": "function",
				"description": "Function that produces an element of the new Array from an element of the current one."
			}, {
				"name": "context",
				"type": "object",
				"description": "Object to use as this when executing callback."
			}],
			"return": "array"
		});

		meta({
			"entity": "method",
			"for": "Array",
			"name": "filter",
			"polyfill": true,
			"description": "Creates a new array with all elements that pass the test implemented by the provided function.",
			"arguments": [{
				"name": "callback",
				"type": "function",
				"description": "Function to test each element of the array."
			}, {
				"name": "context",
				"type": "object",
				"description": "Object to use as this when executing callback."
			}],
			"return": "array"
		});

		var slice = Array.prototype.slice;

		Array.fromArguments = function (args) {
			return slice.call(args, 0);
		};

		if (!Array.prototype.forEach) {
			Array.prototype.forEach = function(callback, context) {
				var i = 0,
					len = this.length;

				for (; i < len; ++i) {
					callback.call(context, this[i], i, this);
				}
			};
		}

		if (!Array.prototype.map) {
			Array.prototype.map = function (callback, context) {
				var mapped = [];
				this.forEach(map);
				function map (element, index, array) {
					mapped[index] = callback.call(context, element, index, array);
				}
				return mapped;
			};
		}

		if (!Array.prototype.filter) {
			Array.prototype.filter = function (callback, context) {
				var filtered = [];
				this.forEach(map);
				function filter (element, index, array) {
					if (callback.call(context, element, index, array)) {
						filtered.push(element);
					}
				}
				return filtered;
			};
		}
	}
);
<|endoftext|>