<commit_before>/*global window*/
/*
 * jQuery.touchSlider.js © 2012 MIT — The Rapportive Team <conrad@rapportive.com>
 * See https://github.com/rapportive-oss/jquery-touchslider for details.
 */
(function ($) {
    /**
     * Cubic Bezier CSS3 transitions emulator
     *
     * See this post for more details
     * http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html
     *
     * Copyright (C) 2011 Nikolay Nemshilov
     */
    function bezier(p1, p2, p3, p4) {
        // defining the bezier functions in the polynomial form
        var Cx = 3 * p1,
            Bx = 3 * (p3 - p1) - Cx,
            Ax = 1 - Cx - Bx,
            Cy = 3 * p2,
            By = 3 * (p4 - p2) - Cy,
            Ay = 1 - Cy - By;

        function bezier_x(t) {
            return t * (Cx + t * (Bx + t * Ax));
        }
        function bezier_y(t) {
            return t * (Cy + t * (By + t * Ay));
        }

        function bezier_x_der(t) {
            return Cx + t * (2 * Bx + 3 * Ax * t);
        }

        // using Newton's method to aproximate the parametric value of x for t
        function find_x_for(t) {
            var x = t,
                i = 0, z;

            while (i < 5) { // making 5 iterations max
                z = bezier_x(x) - t;
                if (Math.abs(z) < 1e-3) {
                    break; // if already got close enough
                }
                x = x - z / bezier_x_der(x);
                i += 1;
            }
            return x;
        }

        function ret(t) {
            return bezier_y(find_x_for(t));
        }
        ret.toCSS = function () {
            return "cubic-bezier(" + [p1, p2, p3, p4].join(", ") + ")";
        };
        return ret;
    }

    // Given that the user is moving the slides at a given velocity,
    // what should the bezier curve animation look like?
    //
    // In order for the user not to notice the transition to the bezier,
    // it's important that we don't move the slide; or change its velocity.
    //
    // Luckily CSS enforces the former for us, by setting the initial point
    // of a cubic to (0, 0). The latter is not hard to ensure either, as we
    // know that a bezier curve at the origin is tangent to the line between
    // its first two control points. Thus, we just have to ensure that the
    // control point lies on the line where velocity = v (or x/t = v).
    //
    // The only decision we have with regards to that control point is how
    // far from the origin it should be; this is basically a number we can
    // make up to adjust the user experience, I've called it "i" as it
    // represents how important the user's initial velocity is to the shape
    // of the final curve.
    //
    //    /|       given: x² + t² = i²
    // i / |       given: x / t = v
    //  /  | x     => x = sqrt(i² * v²/(1 + v²))
    // ,___|
    //   t
    //
    // As we want the animation to finish at the end with 0 velocity, we
    // are forced to put the second intermediate control point with an x
    // coordinate of 1 (the final control point is at (1, 1) by definition),
    // so the only other choice we have for the shape of our curve is how
    // far along the t axis to put the second control point.
    //
    // At the moment I'm using 't' for that, because it seems to result in
    // consistently smooth-feeling curves; but it's just an arbitrary choice.
    //
    // http://cubic-bezier.com/ is useful for getting an intuitive feel for
    // this. Also note that webkit doesn't support "bouncey" transitions
    // (with points outside the (0, 0) <-> (1, 1) range) as of iOS 5.
    function bezier_for_velocity(v) {
        var importance = 0.5,
            x = (v < 0 ? -1 : 1) * Math.sqrt(importance * importance * (v * v / (1 + v * v))),
            t = x / v,
            sameness = t;

        return bezier(t, x, sameness, 1.0);
    }

    $.fn.touchSlider = function () {
        var x, t, initial_x,
            previous_x, previous_t,
            $this = this,
            slides = this.find(".slides"),
            initial_t, initial_slide,
            current_offset = 0, initial_offset,
            target_offset = 0,
            animation_start, animation_duration, animation_timing_function,
            slide_width = this.width(), last_slide = this.find(".slide").length - 1,
            left_edge = 0,
            default_duration = 500,
            right_edge = slide_width * last_slide;

        this.css({
            'overflow': 'hidden'
        });
        slides.css({
            'width': (slide_width * this.find(".slide").length) + 'px',
            '-webkit-transition-property': '-webkit-transform',
            '-webkit-transform': 'translate3D(0, 0, 0)'
        });
        // Force slides into texture buffers on the iPad
        slides.find(".slide").css({
            '-webkit-transform': 'translate3D(0, 0, 0)'
        });

        this.bind('touchstart', function (e) {
            e = e.originalEvent || e;

            if (e.touches.length !== 1) {
                return;
            }

            // Allow the user to interrupt our animation.
            var t = new Date() - animation_start;
            if (animation_start && t < animation_duration) {
                current_offset += animation_timing_function(t / animation_duration) * (target_offset - current_offset);
            } else {
                current_offset = target_offset;
            }

            slides.css({
                // Remove the delay on animation for instant finger feedback
                '-webkit-transition-duration': '0s',
                '-webkit-transform': 'translate3D(' + (0 - current_offset) + 'px, 0, 0)'
            });

            initial_offset = current_offset;
            initial_slide = Math.floor(target_offset / slide_width);
            initial_x = previous_x = x = e.touches[0].clientX;
            initial_t = previous_t = t = new Date();

        }).on('touchmove', function (e) {
            e = e.originalEvent || e;

            if (e.touches.length !== 1) {
                return;
            } else {
                e.preventDefault();
            }

            previous_x = x;
            previous_t = t;
            x = e.touches[0].clientX;
            t = new Date();

            current_offset = initial_offset + (initial_x - x);

            // iOS-like halved velocity when dragging beyond the edge
            if (current_offset < left_edge) {
                current_offset = current_offset / 2;
            } else if (current_offset > right_edge) {
                current_offset = right_edge + (current_offset - right_edge) / 2;
            }

            slides.css({
                '-webkit-transform': 'translate3D(' + (0 - current_offset) + 'px, 0, 0)'
            });

        }).on('touchend', function (e) {
            var target_slide, target_distance, velocity;

            // Was the user dragging this thing around speculatively?
            if (t - initial_t > 250) {
                target_slide = Math.round(current_offset / slide_width);

            // If not, which way did they flick?
            // Leave a bit of freedom so a downward flick does nothing.
            } else if (Math.abs(initial_x - x) < 20) {
                target_slide = initial_slide;
            } else if (x > initial_x) {
                target_slide = initial_slide - 1;
            } else {
                target_slide = initial_slide + 1;
            }

            if (target_slide < 0) {
                target_slide = 0;
            } else if (target_slide > last_slide) {
                target_slide = last_slide;
            }

            target_distance = current_offset - target_slide * slide_width;

            // avoid division by zero
            if (Math.abs(x - previous_x) < 1 || Math.abs(t - previous_t) < 1) {
                velocity = 0.1;
            } else {
                velocity = ((x - previous_x) / (t - previous_t)) * (default_duration / target_distance);
            }

            $this.trigger('slideTo', {slide: target_slide, bezier: bezier_for_velocity(velocity)});

        }).on('slideTo', function (e, opts) {
            target_offset = opts.slide * slide_width;

            // NOTE: 0.01s > 0s, if we set it to 0 then the transition doesn't happen
            // if the first time the thing is shown is with animate:false.
            animation_duration = (opts.animate === false ? 1 : default_duration);
            animation_start = new Date();
            animation_timing_function = opts.bezier || bezier_for_velocity(0.1);

            slides.css({
                '-webkit-transition-timing-function': animation_timing_function.toCSS(),
                '-webkit-transition-duration': animation_duration + 'ms',
                '-webkit-transform': 'translate3D(' + (0 - target_offset) + 'px, 0, 0)'
            });

            $this.trigger('slidingTo', opts.slide);
            window.setTimeout(function () {
                $this.trigger('slidTo', opts.slide);
            }, animation_duration);

        });
        return this;
    };
}(window.jQuery || window.Zepto));
<commit_msg>Make the event API more consistent<commit_after>/*global window*/
/*
 * jQuery.touchSlider.js © 2012 MIT — The Rapportive Team <conrad@rapportive.com>
 * See https://github.com/rapportive-oss/jquery-touchslider for details.
 */
(function ($) {
    /**
     * Cubic Bezier CSS3 transitions emulator
     *
     * See this post for more details
     * http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html
     *
     * Copyright (C) 2011 Nikolay Nemshilov
     */
    function bezier(p1, p2, p3, p4) {
        // defining the bezier functions in the polynomial form
        var Cx = 3 * p1,
            Bx = 3 * (p3 - p1) - Cx,
            Ax = 1 - Cx - Bx,
            Cy = 3 * p2,
            By = 3 * (p4 - p2) - Cy,
            Ay = 1 - Cy - By;

        function bezier_x(t) {
            return t * (Cx + t * (Bx + t * Ax));
        }
        function bezier_y(t) {
            return t * (Cy + t * (By + t * Ay));
        }

        function bezier_x_der(t) {
            return Cx + t * (2 * Bx + 3 * Ax * t);
        }

        // using Newton's method to aproximate the parametric value of x for t
        function find_x_for(t) {
            var x = t,
                i = 0, z;

            while (i < 5) { // making 5 iterations max
                z = bezier_x(x) - t;
                if (Math.abs(z) < 1e-3) {
                    break; // if already got close enough
                }
                x = x - z / bezier_x_der(x);
                i += 1;
            }
            return x;
        }

        function ret(t) {
            return bezier_y(find_x_for(t));
        }
        ret.toCSS = function () {
            return "cubic-bezier(" + [p1, p2, p3, p4].join(", ") + ")";
        };
        return ret;
    }

    // Given that the user is moving the slides at a given velocity,
    // what should the bezier curve animation look like?
    //
    // In order for the user not to notice the transition to the bezier,
    // it's important that we don't move the slide; or change its velocity.
    //
    // Luckily CSS enforces the former for us, by setting the initial point
    // of a cubic to (0, 0). The latter is not hard to ensure either, as we
    // know that a bezier curve at the origin is tangent to the line between
    // its first two control points. Thus, we just have to ensure that the
    // control point lies on the line where velocity = v (or x/t = v).
    //
    // The only decision we have with regards to that control point is how
    // far from the origin it should be; this is basically a number we can
    // make up to adjust the user experience, I've called it "i" as it
    // represents how important the user's initial velocity is to the shape
    // of the final curve.
    //
    //    /|       given: x² + t² = i²
    // i / |       given: x / t = v
    //  /  | x     => x = sqrt(i² * v²/(1 + v²))
    // ,___|
    //   t
    //
    // As we want the animation to finish at the end with 0 velocity, we
    // are forced to put the second intermediate control point with an x
    // coordinate of 1 (the final control point is at (1, 1) by definition),
    // so the only other choice we have for the shape of our curve is how
    // far along the t axis to put the second control point.
    //
    // At the moment I'm using 't' for that, because it seems to result in
    // consistently smooth-feeling curves; but it's just an arbitrary choice.
    //
    // http://cubic-bezier.com/ is useful for getting an intuitive feel for
    // this. Also note that webkit doesn't support "bouncey" transitions
    // (with points outside the (0, 0) <-> (1, 1) range) as of iOS 5.
    function bezier_for_velocity(v) {
        var importance = 0.5,
            x = (v < 0 ? -1 : 1) * Math.sqrt(importance * importance * (v * v / (1 + v * v))),
            t = x / v,
            sameness = t;

        return bezier(t, x, sameness, 1.0);
    }

    $.fn.touchSlider = function () {
        var x, t, initial_x,
            previous_x, previous_t,
            $this = this,
            slides = this.find(".slides"),
            initial_t, initial_slide,
            current_offset = 0, initial_offset,
            target_offset = 0,
            animation = {},
            slide_width = this.width(), last_slide = this.find(".slide").length - 1,
            left_edge = 0,
            default_duration = 500,
            right_edge = slide_width * last_slide;

        this.css({
            'overflow': 'hidden'
        });
        slides.css({
            'width': (slide_width * this.find(".slide").length) + 'px',
            '-webkit-transition-property': '-webkit-transform',
            '-webkit-transform': 'translate3D(0, 0, 0)'
        });
        // Force slides into texture buffers on the iPad
        slides.find(".slide").css({
            '-webkit-transform': 'translate3D(0, 0, 0)'
        });

        this.bind('touchstart', function (e) {
            e = e.originalEvent || e;

            if (e.touches.length !== 1) {
                return;
            }

            // Allow the user to interrupt our animation.
            var t = new Date() - animation.start;
            if (animation && t < animation.duration) {
                current_offset += animation.bezier(t / animation.duration) * (target_offset - current_offset);
            } else {
                current_offset = target_offset;
            }

            slides.css({
                // Remove the delay on animation for instant finger feedback
                '-webkit-transition-duration': '0s',
                '-webkit-transform': 'translate3D(' + (0 - current_offset) + 'px, 0, 0)'
            });

            initial_offset = current_offset;
            initial_slide = Math.floor(target_offset / slide_width);
            initial_x = previous_x = x = e.touches[0].clientX;
            initial_t = previous_t = t = new Date();

        }).on('touchmove', function (e) {
            e = e.originalEvent || e;

            if (e.touches.length !== 1) {
                return;
            } else {
                e.preventDefault();
            }

            previous_x = x;
            previous_t = t;
            x = e.touches[0].clientX;
            t = new Date();

            current_offset = initial_offset + (initial_x - x);

            // iOS-like halved velocity when dragging beyond the edge
            if (current_offset < left_edge) {
                current_offset = current_offset / 2;
            } else if (current_offset > right_edge) {
                current_offset = right_edge + (current_offset - right_edge) / 2;
            }

            slides.css({
                '-webkit-transform': 'translate3D(' + (0 - current_offset) + 'px, 0, 0)'
            });

        }).on('touchend', function (e) {
            var target_slide, target_distance, velocity;

            // Was the user dragging this thing around speculatively?
            if (t - initial_t > 250) {
                target_slide = Math.round(current_offset / slide_width);

            // If not, which way did they flick?
            // Leave a bit of freedom so a downward flick does nothing.
            } else if (Math.abs(initial_x - x) < 20) {
                target_slide = initial_slide;
            } else if (x > initial_x) {
                target_slide = initial_slide - 1;
            } else {
                target_slide = initial_slide + 1;
            }

            if (target_slide < 0) {
                target_slide = 0;
            } else if (target_slide > last_slide) {
                target_slide = last_slide;
            }

            target_distance = current_offset - target_slide * slide_width;

            // avoid division by zero
            if (Math.abs(x - previous_x) < 1 || Math.abs(t - previous_t) < 1) {
                velocity = 0.1;
            } else {
                velocity = ((x - previous_x) / (t - previous_t)) * (default_duration / target_distance);
            }

            $this.trigger('slideTo', {slide: target_slide, bezier: bezier_for_velocity(velocity)});

        }).on('slideTo', function (e, opts) {
            target_offset = opts.slide * slide_width;

            animation = {
                slide: opts.slide,
                // webkit doesn't perform the transition if the duration is 0 and the slider is offscreen
                duration: Math.max(typeof opts.duration === 'undefined' ? default_duration : opts.duration, 1),
                start: new Date(),
                bezier: opts.bezier || bezier_for_velocity(0.1)
            };

            slides.css({
                '-webkit-transition-timing-function': animation.bezier.toCSS(),
                '-webkit-transition-duration': animation.duration + 'ms',
                '-webkit-transform': 'translate3D(' + (0 - target_offset) + 'px, 0, 0)'
            });

            $this.trigger('slidingTo', animation);
            window.setTimeout(function () {
                $this.trigger('slidTo', animation);
            }, animation.duration);

        });
        return this;
    };
}(window.jQuery || window.Zepto));
<|endoftext|>


<commit_before>function QRLogin(args, lang, controls  /* = qr,download,restore,header*/) {

//	var qrcodeBox = document.getElementById("qrcode");
	var curcode = null;
	var challengeUrl = null;
	var useEventSource = false;
	var connection = null;
	var eventSource = null;
	var curmode = false;
	var restpanel = false;
	var downloadshown = false;
	var jsredir = false;

	var qrcodeBox = controls["qr"];
	var restoreBox = controls["restore"];
	var downloadBox = controls["download"];

	var qrcode = new QRCode(qrcodeBox, {
	    useSVG: true, correctLevel: 0
	  });

	var fileinput = restoreBox.getElementsByTagName("input")[0];

	function base64EncodeUrl(str){
	    return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
	}

	function getDomainFromUrl(url) {
		var parts = url.split('/');
		//proto: / <empty> / <auth>@<domain>:<port> / path....

		var authdomain = parts[2].split('@');
		var domain = authdomain[authdomain.length-1];

		return domain;

	}

	var apikey = getDomainFromUrl(args.redirect_uri);

	this.reload = function(manage) {
	    curmode = manage;
	    restoreBox.hide(); restpanel = false;
	    downloadBox.hide(); downloadshown = false;

	    var bytes = secureRandom(20);
	    curcode = base64EncodeUrl(Crypto.util.bytesToBase64(bytes));
	    var logurl = "l?c=" + Crypto.SHA256(curcode);

	    if (window.EventSource) {
	        if (eventSource != null) eventSource.close();
	        eventSource = new EventSource(logurl);
	        eventSource.addEventListener("message", function(event) {
	            var response = event.data;
	            this.processResponse(response);
	            eventSource.close();
	        } .bind(this));
	        eventSource.addEventListener("error", function(event) {
	            if (eventSource.readyState == 2) {
	                qrcodeBox.style.visibility = "hidden";
	            }
	        } .bind(this))
	    } else {
	        if (connection != null) connection.abort();
	        connection = new XMLHttpRequest();
	        connection.open("GET", logurl, true);
	        connection.onreadystatechange = function(request) {
	            if (connection.readyState == 4) {
	                if (connection.status == 200) {
	                    var response = connection.responseText;
	                    this.processResponse(response);
	                } else if (connection.status == 409) {
	                    this.reload(curmode);
	                }
	            }
	        } .bind(this);
	        connection.send();
	    }

	    if (manage) {
	        challengeUrl = getFullUrl("m#" + lang + "," + apikey + "," + curcode);
	    } else {
	        challengeUrl = getFullUrl("c#" + lang + "," + apikey + "," + curcode);
	    }
	    qrcode.makeCode(challengeUrl)
	}

	qrcodeBox.addEventListener("click",function() {

		var target = qrcodeBox.parentElement;
		target.style.position="relative";
		var iframe = document.createElement("IFRAME");
		iframe.style.width="100%";
		iframe.style.height="100%";
		iframe.style.border="0";
		iframe.style.position="absolute";
		iframe.style.backgroundColor="white";
		iframe.style.left="0";
		iframe.style.top="0";
		iframe.src=challengeUrl;
		target.appendChild(iframe);
		var xbutton = document.createElement("BUTTON");
		xbutton.style.width="20px";
		xbutton.style.height="20px";
		xbutton.style.border="0";
		xbutton.appendChild(document.createTextNode("X"));
		xbutton.style.position="absolute";
		xbutton.style.top="5px";
		xbutton.style.right="5px";
		target.appendChild(xbutton);
		xbutton.addEventListener("click",function() {
			iframe.parentElement.removeChild(iframe);
			xbutton.parentElement.removeChild(xbutton);
		})
	})

	this.showDownloadBox = function() {

	    downloadBox.show();
	    downloadshown = true;
	    var timer = 61;

	    function decTimer() {
	        timer--;
	        if (!downloadshown) return;
	        if (timer == 0) {
	            this.reload(curmode);
	        } else {

	            var e = downloadBox.getElementsByTagName("span")[0];
	            e.removeChild(e.firstChild);
	            e.appendChild(document.createTextNode(timer));
	            setTimeout(bindDecTimer, 1000);
	        }
	    }
	    var bindDecTimer =  decTimer.bind(this);

	    setTimeout(bindDecTimer, 1);
	}

	function printKey() {
	    window.open("print.html#" + lang + "," + apikey + "," + curcode);
	    this.reload(curmode);
	}

	function downloadKey() {
	    location.href = "backup?c="+curcode;
	    this.reload(curmode);
	}

	this.processResponse = function(response) {
	    var r = response.trim();
	    if (r == "") {
	        this.reload(curmode);
	        return;
	    }

	    var redir;
	    if (r.substr(0, 7) == "backup:") {

	        this.showDownloadBox();

	    } else {
	        redir = args.redirect_uri;
	        if (jsredir) {
	            redir = redir + "#?";
	        } else {
	            var qmark = redir.indexOf('?');
	            if (qmark == -1) redir = redir + "?"; else redir = redir + '&';
	        }
	        var adjr = r + ":" + curcode;
	        redir = redir + "code=" + encodeURIComponent(adjr);
	        if (args.scope) redir = redir + "&scope=" + encodeURIComponent(args.scope);
	        if (args.state) redir = redir + "&state=" + encodeURIComponent(args.state);
	        window.top.location = redir;
	    }

	}

	this.setLang = function(l) {
	    lang = l;
	    if (!restpanel && !downloadshown) this.reload(curmode);
	    else if (restpanel) restoreBox.show();
	}

	this.restoreBackup = function() {
	    var target = qrcodeBox.parentElement;
	    target.style.position = "relative";
	    target.appendChild(restoreBox);
	    restoreBox.show();
	    fileinput.classList.remove("error");
	    restpanel = true;
	    downloadBox.hide(); downloadshown = false;

	}

	var init = function() {

	    fileinput.addEventListener("change", uploadFile.bind(this, fileinput));
	    var buttons = downloadBox.getElementsByTagName("button");
	    buttons[0].addEventListener("click", downloadKey.bind(this));
	    buttons[1].addEventListener("click", printKey.bind(this));

	}

	var uploadFile = function(fileinput) {
	    window.browsedFile = fileinput;
	    var files = fileinput.files;
	    if (files.length == 1) {
	        var f = files[0];
	        if (f.size > 150) {
	            fileinput.classList.add("error");
	        } else {
	            var r = new FileReader();
	            r.onload = function(e) {
	                var contents = base64_encodeURIComponent(e.target.result);
	                challengeUrl = getFullUrl("k#" + lang + "," + apikey + "," + contents);
	                qrcode.makeCode(challengeUrl);
	                qrcodeBox.style.visibility = "visible";
	                restoreBox.hide();
	            }
	            r.readAsText(f);
	        }
	    }
	}

	this.enableJSRedir = function (e) {
	    jsredir = e;
	}

	init.call(this);
}

function LangPanel(panel,curlang, qrlogin) {
    var lang = curlang;
    var langlist = ["cs","en"];
    var i;
    var lpanel = panel;

    function drawPanel() {
        while(lpanel.firstChild) {
            lpanel.removeChild(panel.firstChild);
        }

        for (i = 0; i < langlist.length;i++) {
            if (langlist[i] == lang) continue;
            var img = document.createElement("img");

            img.src = "img/lang_"+langlist[i]+".gif";
            img.onclick = function(l) {
                qrlogin.setLang(l);
                lang = l;
                drawPanel();
                loadLang(lang);
                localStorage["default_lang"] = lang;
            } .bind(this, langlist[i]);
            lpanel.appendChild(img);

        }
    }
    drawPanel();
}

function start() {

	var querystr = getQueryString(location.search);
	var lang = querystr["lang"];
	if (!lang) {
	    if (localStorage) lang = localStorage["default_lang"];
	    if (!lang)
		    lang = "en";
	}
	loadLang(lang);
	var js = querystr["js"];
	if (!js) js = false;

	var str_login = getBlockById("tab_login");
	var str_manage = getBlockById("tab_manage");
	var str_restorebackup = getBlockById("tab_restore");
	var qrblock = getBlockById("qrblock");
	var panel = getBlockById("langpanel");
	var restoreBox = getBlockById("restoreform");
	var downloadBox = getBlockById("downloadask");

	window.qrlogin = new QRLogin(querystr, lang,
			{qr:qrblock, restore:restoreBox,download:downloadBox});
	window.qrlogin.reload(false);
	window.qrlogin.enableJSRedir(js);

	str_login.classList.add("hl");
	str_login.addEventListener("click",function() {
		str_login.classList.add("hl");
		str_manage.classList.remove("hl");
		str_restorebackup.classList.remove("hl");
		qrlogin.reload(false);
	});
	str_manage.addEventListener("click",function() {
		str_manage.classList.add("hl");
		str_login.classList.remove("hl");
		str_restorebackup.classList.remove("hl");
		qrlogin.reload(true);
	});
	str_restorebackup.addEventListener("click",function() {
		str_login.classList.remove("hl");
		str_manage.classList.remove("hl");
		str_restorebackup.classList.add("hl");
		qrlogin.restoreBackup();
	});

	var langpanel = new LangPanel(panel,lang,qrlogin);

}
<commit_msg>auth page can now accept query through the hash using #? instead ?. It also updates itself without reloading when hash is changed.<commit_after>function QRLogin(argss, lang, controls  /* = qr,download,restore,header*/) {

//	var qrcodeBox = document.getElementById("qrcode");
	var curcode = null;
	var challengeUrl = null;
	var useEventSource = false;
	var connection = null;
	var eventSource = null;
	var curmode = false;
	var restpanel = false;
	var downloadshown = false;
	var args = argss;
	var closeIFrameButt = function () { };

	var qrcodeBox = controls["qr"];
	var restoreBox = controls["restore"];
	var downloadBox = controls["download"];

	var qrcode = new QRCode(qrcodeBox, {
	    useSVG: true, correctLevel: 0
	  });

	var fileinput = restoreBox.getElementsByTagName("input")[0];

	function base64EncodeUrl(str){
	    return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
	}

	function getDomainFromUrl(url) {
		var parts = url.split('/');
		//proto: / <empty> / <auth>@<domain>:<port> / path....

		var authdomain = parts[2].split('@');
		var domain = authdomain[authdomain.length-1];

		return domain;

	}

	var apikey = null;

	this.reload = function(manage) {
	    apikey = getDomainFromUrl(args.redirect_uri);
	    curmode = manage;
	    restoreBox.hide(); restpanel = false;
	    downloadBox.hide(); downloadshown = false;
	    closeIFrameButt();
	    qrcodeBox.style.visibility = "visible";

	    var bytes = secureRandom(20);
	    curcode = base64EncodeUrl(Crypto.util.bytesToBase64(bytes));
	    var logurl = "l?c=" + Crypto.SHA256(curcode);

	    if (window.EventSource) {
	        if (eventSource != null) eventSource.close();
	        eventSource = new EventSource(logurl);
	        eventSource.addEventListener("message", function(event) {
	            var response = event.data;
	            this.processResponse(response);
	            eventSource.close();
	        } .bind(this));
	        eventSource.addEventListener("error", function(event) {
	            if (eventSource.readyState == 2) {
	                qrcodeBox.style.visibility = "hidden";
	            }
	        } .bind(this))
	    } else {
	        if (connection != null) connection.abort();
	        connection = new XMLHttpRequest();
	        connection.open("GET", logurl, true);
	        connection.onreadystatechange = function(request) {
	            if (connection.readyState == 4) {
	                if (connection.status == 200) {
	                    var response = connection.responseText;
	                    this.processResponse(response);
	                } else if (connection.status == 409) {
	                    this.reload(curmode);
	                }
	            }
	        } .bind(this);
	        connection.send();
	    }

	    if (manage) {
	        challengeUrl = getFullUrl("m#" + lang + "," + apikey + "," + curcode);
	    } else {
	        challengeUrl = getFullUrl("c#" + lang + "," + apikey + "," + curcode);
	    }
	    qrcode.makeCode(challengeUrl)
	}

	qrcodeBox.addEventListener("click",function() {

	    var target = qrcodeBox.parentElement;
	    target.style.position="relative";
	    var iframe = document.createElement("IFRAME");
	    iframe.style.width="100%";
	    iframe.style.height="100%";
	    iframe.style.border="0";
	    iframe.style.position="absolute";
	    iframe.style.backgroundColor="white";
	    iframe.style.left="0";
	    iframe.style.top="0";
	    iframe.src=challengeUrl;
	    target.appendChild(iframe);
	    var xbutton = document.createElement("BUTTON");
	    xbutton.style.width="20px";
	    xbutton.style.height="20px";
	    xbutton.style.border="0";
	    xbutton.appendChild(document.createTextNode("X"));
	    xbutton.style.position="absolute";
	    xbutton.style.top="5px";
	    xbutton.style.right="5px";
	    target.appendChild(xbutton);
	    closeIFrameButt = function() {
	        iframe.parentElement.removeChild(iframe);
	        xbutton.parentElement.removeChild(xbutton);
	        closeIFrameButt = function () { };
	    };
	    xbutton.addEventListener("click", closeIFrameButt);
	});

	this.showDownloadBox = function() {

	    downloadBox.show();
	    downloadshown = true;
	    var timer = 61;

	    function decTimer() {
	        timer--;
	        if (!downloadshown) return;
	        if (timer == 0) {
	            this.reload(curmode);
	        } else {

	            var e = downloadBox.getElementsByTagName("span")[0];
	            e.removeChild(e.firstChild);
	            e.appendChild(document.createTextNode(timer));
	            setTimeout(bindDecTimer, 1000);
	        }
	    }
	    var bindDecTimer =  decTimer.bind(this);

	    setTimeout(bindDecTimer, 1);
	}

	function printKey() {
	    window.open("print.html#" + lang + "," + apikey + "," + curcode);
	    this.reload(curmode);
	}

	function downloadKey() {
	    location.href = "backup?c="+curcode;
	    this.reload(curmode);
	}

	this.processResponse = function(response) {
	    var r = response.trim();
	    if (r == "") {
	        this.reload(curmode);
	        return;
	    }

	    var redir;
	    if (r.substr(0, 7) == "backup:") {

	        this.showDownloadBox();

	    } else {
	        redir = args.redirect_uri;
	        if (args.js) {
	            redir = redir + "#?";
	        } else {
	            var qmark = redir.indexOf('?');
	            if (qmark == -1) redir = redir + "?"; else redir = redir + '&';
	        }
	        var adjr = r + ":" + curcode;
	        redir = redir + "code=" + encodeURIComponent(adjr);
	        if (args.scope) redir = redir + "&scope=" + encodeURIComponent(args.scope);
	        if (args.state) redir = redir + "&state=" + encodeURIComponent(args.state);
	        window.top.location = redir;
	    }

	}

	this.setLang = function(l) {
	    lang = l;
	    if (!restpanel && !downloadshown) this.reload(curmode);
	    else if (restpanel) restoreBox.show();
	}

	this.setNewArgs = function(argss) {
	    args = argss;
	}

	this.restoreBackup = function() {
	    var target = qrcodeBox.parentElement;
	    target.style.position = "relative";
	    target.appendChild(restoreBox);
	    closeIFrameButt();
	    restoreBox.show();
	    fileinput.classList.remove("error");
	    restpanel = true;
	    downloadBox.hide(); downloadshown = false;

	}

	var init = function() {

	    fileinput.addEventListener("change", uploadFile.bind(this, fileinput));
	    var buttons = downloadBox.getElementsByTagName("button");
	    buttons[0].addEventListener("click", downloadKey.bind(this));
	    buttons[1].addEventListener("click", printKey.bind(this));

	}

	var uploadFile = function(fileinput) {
	    window.browsedFile = fileinput;
	    var files = fileinput.files;
	    if (files.length == 1) {
	        var f = files[0];
	        if (f.size > 150) {
	            fileinput.classList.add("error");
	        } else {
	            var r = new FileReader();
	            r.onload = function(e) {
	                var contents = base64_encodeURIComponent(e.target.result);
	                challengeUrl = getFullUrl("k#" + lang + "," + apikey + "," + contents);
	                qrcode.makeCode(challengeUrl);
	                qrcodeBox.style.visibility = "visible";
	                restoreBox.hide();
	            }
	            r.readAsText(f);
	        }
	    }
	}

	init.call(this);
}

function LangPanel(panel,curlang, qrlogin) {
    var lang = curlang;
    var langlist = ["cs","en"];
    var i;
    var lpanel = panel;

    function drawPanel() {
        while(lpanel.firstChild) {
            lpanel.removeChild(panel.firstChild);
        }

        for (i = 0; i < langlist.length;i++) {
            if (langlist[i] == lang) continue;
            var img = document.createElement("img");

            img.src = "img/lang_"+langlist[i]+".gif";
            img.onclick = function(l) {
                qrlogin.setLang(l);
                lang = l;
                drawPanel();
                loadLang(lang);
                localStorage["default_lang"] = lang;
            } .bind(this, langlist[i]);
            lpanel.appendChild(img);

        }
    }
    this.setLang = function(l) {
        lang = l;
        drawPanel();
        localStorage["default_lang"] = lang;
    }

    drawPanel();
}

function initObjects() {
    var querystr = location.hash.substr(0, 2) == "#?" ? getQueryString(location.hash.substr(1)) : getQueryString(location.search);
    var lang = querystr["lang"];
    if (!lang) {
        if (localStorage) lang = localStorage["default_lang"];
        if (!lang)
            lang = "en";
    }
    loadLang(lang);

    var qrblock = getBlockById("qrblock");
    var panel = getBlockById("langpanel");
    var restoreBox = getBlockById("restoreform");
    var downloadBox = getBlockById("downloadask");

    window.qrlogin = new QRLogin(querystr, lang,
			{ qr: qrblock, restore: restoreBox, download: downloadBox });
    window.qrlogin.reload(false);

    window.langpanel = new LangPanel(panel, lang, qrlogin);
    window.selector = new Selector([
        getBlockById("tab_login"),
        getBlockById("tab_manage"),
        getBlockById("tab_restore")], [
            function () { window.qrlogin.reload(false); },
            function () { window.qrlogin.reload(true); },
            function () { qrlogin.restoreBackup();; }
        ]);
    window.selector.select(0);

}

var Selector = function (switches, actions) {

    this.select = function (v1) {
        for (var i = 0; i < switches.length; i++) {
            if (i == v1) switches[i].classList.add("hl");
            else switches[i].classList.remove("hl");
        }
    }
    for (var i = 0; i < switches.length; i++) {
        var fn = function (v) {
            this.select(v);
            actions[v]();
        }.bind(this, i);
        switches[i].addEventListener("click", fn);
    }

};

function start() {
    initObjects();
}

window.addEventListener("hashchange", function () {
    if (location.hash.substr(0, 2) == "#?") {
        var querystr = location.hash.substr(0, 2) == "#?" ? getQueryString(location.hash.substr(1)) : getQueryString(location.search);
        var lang = querystr["lang"];
        if (lang) {
            window.langpanel.setLang(lang);
            loadLang(lang);
        }
        window.qrlogin.setNewArgs(querystr);
        window.qrlogin.reload(false);
        window.selector.select(0);
    }
});<|endoftext|>


<commit_before>(function($) {

  function TypeSuggest(el, options){
    var defaults = {
      data: undefined,
      min_characters: 2,
      display_key: false,
      display_keys: undefined,
      search_keys: undefined,
      ajax: false,
      url: '',
      param: 'q',
      params: '',
      beforeSend: undefined,
      callback: undefined,
      debounce: 250,
      max_suggest: 5
    };

    this.config = $.extend({}, defaults, options);

    // set parent element
    this.$el = $(el).addClass("typesuggest");

    this.data = this.config.data;

    this.init();
    this.events();

  }

  TypeSuggest.prototype.init = function(){
    var self = this;

    self.$fields = self.$el.find("input").addClass("typesuggest-input").attr('autocomplete', 'off');
    self.$suggest = $('
').appendTo(self.$el);
  }

  TypeSuggest.prototype.events = function(){
    var self = this;

    self.$fields.on('keydown', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      // if up or down arrows, scroll thru list. if enter key, select item
      if(key == 40 || key == 38 || key == 13){

        if(e.keyCode == 40){ // down arrow

          self.$suggest.addClass("selecting");
          self.move(+1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 38){ // up arrow

          self.$suggest.addClass("selecting");
          self.move(-1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 13){ // enter

          var $selectedItem = self.$suggest.find(".selected");
          var data = $selectedItem.data();

          self.selectItem(data);

          // prevent caret from moving
          return false;

        }else{ // everything else

          self.$suggest.removeClass("selecting");

        }

      }

    })

    self.$fields.on('keyup', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      self.$currentField = $field;

      // if esc key, hide suggestions
      if(key === 27){
        self.updateList(true);
      }

      // if not up or down arrows or enter key, fill suggestion
      if(key !== 40 && key !== 38 && key !== 13 && key !== 27){

        if(!self.debounce){

          // get value
          var val = $field.val();

          self.options = [];

          if(val.length && val.length >= self.config.min_characters){

            self.startDebounce();

            if(self.config.ajax){
              if(self.config.beforeSend){
                val = self.config.beforeSend(val);
              }

              if(self.config.url.indexOf('?') > -1){
                var url = self.config.url + "&" + self.config.params + self.config.param + "=" + val;
              }else{
                var url = self.config.url + "?" + self.config.params + self.config.param + "=" + val;
              }

              $.ajax({
                url: url,
                type: 'GET',
                dataType: 'json',
                success: function(data, textStatus, xhr){
                  self.options = self.config.results_key && data[self.config.results_key] || data;

                  self.updateList();
                }
              })
            }else{
              // compare value to provided list
              $.each(self.data, function(i, item){
                var valLower = val.toLowerCase();
                var itemLower;

                if(self.config.search_keys){
                  $.each(self.config.search_keys, function(i, search_key){
                    // type check search keys, checking for string or array (object)
                    if(typeof item[search_key] === 'string'){
                      itemLower = item[search_key].toLowerCase();
                    }

                    if(typeof item[search_key] === 'object' && item[search_key].length){
                      $.each(item[search_key], function(i, keyword){
                        itemLower = keyword.toLowerCase();
                      })
                    }
                  })
                }else{
                  itemLower = item.toLowerCase();
                }

                if(itemLower.search(valLower) > -1){
                  self.options.push(item);
                }
              })

            }

          }

          self.updateList();
        }
      }else{
        // prevent caret from moving
        return false;
      }
    })

    self.$suggest.on('mouseover', 'li', function(){
      var $li = $(this);

      self.$suggest.find("li").removeClass("selected");
      $li.addClass("selected");
    })

    // if click outside the box, hide suggestions
    $('html').on('click', function(){
      self.updateList(true);
    })
  }

  TypeSuggest.prototype.move = function(where){
    var self = this;
    var $selectedItem = self.$suggest.find(".selected");
    var firstSelected = self.$suggest.find("li:first").hasClass("selected");
    var lastSelected = self.$suggest.find("li:last").hasClass("selected");

    if($selectedItem.length && where > 0){

      if(lastSelected){
        // select first
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:first").addClass("selected");
      }else{
        // selection exists, go to next
        $selectedItem.removeClass("selected").next("li").addClass("selected")
      }

    }
    else if($selectedItem.length && where < 0){

      if(firstSelected){
        // select last
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:last").addClass("selected");
      }else{
        // selection exists, go to prev
        $selectedItem.removeClass("selected").prev("li").addClass("selected")
      }

    }
    else{

      // select first
      self.$suggest.find("li:first").addClass("selected");

    }
  }

  TypeSuggest.prototype.parseDisplay = function(option, display_keys){
    var self = this;
    var output = [];
    var parseKeys = function(keys){

      $.each(keys, function(i, key){

        if(typeof key === 'object'){

          output.push('');

          parseKeys(key.keys && key.keys);

          output.push('');

        }else{

          if(option[key]){
            output.push('' + option[key] + '');
          }

        }

      })

    }

    parseKeys(display_keys);

    return output.join('');
  }

  TypeSuggest.prototype.updateList = function(clear){
    var self = this;
    var $list = $('
');

    if(self.options && self.options.length && !clear){
      self.$suggest.addClass("selecting");

      var options = self.options;
      $.each(options, function(i, option){
        if(i < self.config.max_suggest){
          var $listItem = $("
").data("option", option);

          if(self.config.display_keys){
            $listItem.html(self.parseDisplay(option, self.config.display_keys));
          }else{
            $listItem.html(option);
          }

          $list.append($listItem);

          $listItem.on('click', function(){
            self.selectItem({option: option});
          })
        }
      })

      self.$suggest.html($list);

    }else{
      self.$suggest.removeClass("selecting").empty();;
    }
  }

  TypeSuggest.prototype.selectItem = function(data){
    var self = this;

    self.options = [];
    self.updateList(true);

    if(self.config.display_key){
      self.$currentField.val(data.option[self.config.display_key]);
    }else if(self.config.display_key === false){
      self.$currentField.val(data.option);
    }

    self.config.callback && self.config.callback(data);
  }

  TypeSuggest.prototype.startDebounce = function(){
    var self = this;

    self.debounce = true;

    setTimeout(function(){
      self.debounce = false;
    }, self.config.debounce)
  }

  $.fn.suggest = function(options){
    var suggest = new TypeSuggest(this, options);
    return this;
  }

})(jQuery);
<commit_msg>add use case for typing and hitting enter without selecting an option<commit_after>(function($) {

  function TypeSuggest(el, options){
    var defaults = {
      data: undefined,
      min_characters: 2,
      display_key: false,
      display_keys: undefined,
      search_keys: undefined,
      ajax: false,
      url: '',
      param: 'q',
      params: '',
      beforeSend: undefined,
      callback: undefined,
      debounce: 250,
      max_suggest: 5
    };

    this.config = $.extend({}, defaults, options);

    // set parent element
    this.$el = $(el).addClass("typesuggest");

    this.data = this.config.data;

    this.init();
    this.events();

  }

  TypeSuggest.prototype.init = function(){
    var self = this;

    self.$fields = self.$el.find("input").addClass("typesuggest-input").attr('autocomplete', 'off');
    self.$suggest = $('
').appendTo(self.$el);
  }

  TypeSuggest.prototype.events = function(){
    var self = this;

    self.$fields.on('keydown', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;
      var val = $field.val();

      // if up or down arrows, scroll thru list. if enter key, select item
      if(key == 40 || key == 38 || key == 13){

        if(e.keyCode == 40){ // down arrow

          self.$suggest.addClass("selecting");
          self.move(+1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 38){ // up arrow

          self.$suggest.addClass("selecting");
          self.move(-1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 13){ // enter

          var $selectedItem = self.$suggest.find(".selected");

          if($selectedItem.length){
            // if user selects an item from the list
            var data = $selectedItem.data();

            self.selectItem(data);

          }else if(val.length){
            // if user does not select an item from the list, search
            self.fetchList(val, function(){

              // then, if there is only one result, continue as if they had selected that result
              if(self.options.length === 1){

                self.selectItem({ option: self.options[0] });

              }

            });
          }

          // prevent caret from moving
          return false;

        }else{ // everything else

          self.$suggest.removeClass("selecting");

        }

      }

    })

    self.$fields.on('keyup', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      self.$currentField = $field;

      // if esc key, hide suggestions
      if(key === 27){
        self.updateList(true);
      }

      // if not up or down arrows or enter key, fill suggestion
      if(key !== 40 && key !== 38 && key !== 13 && key !== 27){

        if(!self.debounce){

          // get value
          var val = $field.val();

          self.fetchList(val, function(){
            self.updateList();
          });

          self.updateList();

        }
      }else{
        // prevent caret from moving
        return false;
      }
    })

    self.$suggest.on('mouseover', 'li', function(){
      var $li = $(this);

      self.$suggest.find("li").removeClass("selected");
      $li.addClass("selected");
    })

    // if click outside the box, hide suggestions
    $('html').on('click', function(){
      self.updateList(true);
    })
  }

  TypeSuggest.prototype.fetchList = function(val, callback){
    var self = this;

    self.options = [];

    if(val.length && val.length >= self.config.min_characters){

      self.startDebounce();

      if(self.config.ajax){
        if(self.config.beforeSend){
          val = self.config.beforeSend(val);
        }

        if(self.config.url.indexOf('?') > -1){
          var url = self.config.url + "&" + self.config.params + self.config.param + "=" + val;
        }else{
          var url = self.config.url + "?" + self.config.params + self.config.param + "=" + val;
        }

        $.ajax({
          url: url,
          type: 'GET',
          dataType: 'json',
          success: function(data, textStatus, xhr){
            self.options = self.config.results_key && data[self.config.results_key] || data;

            typeof callback === 'function' && callback();
          }
        })
      }else{
        // compare value to provided list
        $.each(self.data, function(i, item){
          var valLower = val.toLowerCase();
          var itemLower;

          if(self.config.search_keys){
            $.each(self.config.search_keys, function(i, search_key){
              // type check search keys, checking for string or array (object)
              if(typeof item[search_key] === 'string'){
                itemLower = item[search_key].toLowerCase();
              }

              if(typeof item[search_key] === 'object' && item[search_key].length){
                $.each(item[search_key], function(i, keyword){
                  itemLower = keyword.toLowerCase();
                })
              }
            })
          }else{
            itemLower = item.toLowerCase();
          }

          if(itemLower.search(valLower) > -1){
            self.options.push(item);

            typeof callback === 'function' && callback();
          }
        })

      }

    }
  }

  TypeSuggest.prototype.move = function(where){
    var self = this;
    var $selectedItem = self.$suggest.find(".selected");
    var firstSelected = self.$suggest.find("li:first").hasClass("selected");
    var lastSelected = self.$suggest.find("li:last").hasClass("selected");

    if($selectedItem.length && where > 0){

      if(lastSelected){
        // select first
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:first").addClass("selected");
      }else{
        // selection exists, go to next
        $selectedItem.removeClass("selected").next("li").addClass("selected")
      }

    }
    else if($selectedItem.length && where < 0){

      if(firstSelected){
        // select last
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:last").addClass("selected");
      }else{
        // selection exists, go to prev
        $selectedItem.removeClass("selected").prev("li").addClass("selected")
      }

    }
    else{

      // select first
      self.$suggest.find("li:first").addClass("selected");

    }
  }

  TypeSuggest.prototype.parseDisplay = function(option, display_keys){
    var self = this;
    var output = [];
    var parseKeys = function(keys){

      $.each(keys, function(i, key){

        if(typeof key === 'object'){

          output.push('');

          parseKeys(key.keys && key.keys);

          output.push('');

        }else{

          if(option[key]){
            output.push('' + option[key] + '');
          }

        }

      })

    }

    parseKeys(display_keys);

    return output.join('');
  }

  TypeSuggest.prototype.updateList = function(clear){
    var self = this;
    var $list = $('
');

    if(self.options && self.options.length && !clear){
      self.$suggest.addClass("selecting");

      var options = self.options;
      $.each(options, function(i, option){
        if(i < self.config.max_suggest){
          var $listItem = $("
").data("option", option);

          if(self.config.display_keys){
            $listItem.html(self.parseDisplay(option, self.config.display_keys));
          }else{
            $listItem.html(option);
          }

          $list.append($listItem);

          $listItem.on('click', function(){
            self.selectItem({option: option});
          })
        }
      })

      self.$suggest.html($list);

    }else{
      self.$suggest.removeClass("selecting").empty();;
    }
  }

  TypeSuggest.prototype.selectItem = function(data){
    var self = this;

    self.options = [];
    self.updateList(true);

    if(self.config.display_key){
      self.$currentField.val(data.option[self.config.display_key]);
    }else if(self.config.display_key === false){
      self.$currentField.val(data.option);
    }

    self.config.callback && self.config.callback(data);
  }

  TypeSuggest.prototype.startDebounce = function(){
    var self = this;

    self.debounce = true;

    setTimeout(function(){
      self.debounce = false;
    }, self.config.debounce)
  }

  $.fn.suggest = function(options){
    var suggest = new TypeSuggest(this, options);
    return this;
  }

})(jQuery);
<|endoftext|>


<commit_before>/*
==================================================================================================
  Jasy - JavaScript Tooling Framework
  Copyright 2010-2011 Sebastian Werner
==================================================================================================
*/

(function(global, toString, undef)
{
	if (!global.jasy) {
		global.jasy = {};
	}

	/**
	 * Test module
	 *
	 * It also bundles all fixes under "fix/" before starting with the real code.
	 *
	 * @require {fix.ArrayIndexOf}
	 * @require {fix.Console}
	 * @require {fix.DateNow}
	 * @require {fix.DocumentHead}
	 * @require {fix.ExecScript}
	 * @require {fix.FunctionBind}
	 * @require {fix.HTML5Markup}
	 * @require {fix.ObjectKeys}
	 * @require {fix.SetTimeoutArgs}
	 */
	var Test = jasy.Test =
	{
		/**
		 * Adds a new assertion check
		 *
		 * @param func {Function} Function for the test. Must return boolean.
		 * @param methodName {String} Name of the method to attach.
		 * @param assertMsg {}
		 *
		 */
		add : function(func, methodName, assertMsg)
		{
			if(!func) {
				throw new Error("Invalid function during adding assertion for " + methodName);
			}

			// Attach given method as is to assertion
			this[methodName] = func;
			if(func.displayName == null) {
				func.displayName = "jasy.Test." + methodName;
			}

			// Build assert method name
			var assertName = "assert";
			if (methodName.substring(0, 2) == "is") {
				assertName += methodName.slice(2);
			} else {
				assertName += methodName.charAt(0).toUpperCase() + methodName.slice(1);
			}

			// Wrap method throw error for simplified throwing of exceptions in type checks
			if (func.length == 1)
			{
				this[assertName] = function(value, customMsg)
				{
					if (!func(value)) {
						throw new TypeError('Value: "' + value + '": ' + (customMsg||assertMsg));
					}
				};
			}
			else
			{
				this[assertName] = function(value, test, customMsg)
				{
					if (!func(value, test))
					{
						var msg = (customMsg||assertMsg).replace("%1", ""+test);
						throw new TypeError('Value: "' + value + '": ' + msg);
					}
				};
			}

			this[assertName].displayName = "jasy.Test." + assertName;
		}
	};

	Test.add(function(value) { return typeof value == "boolean"; }, "isBoolean", "Not boolean!");
	Test.add(function(value) { return value === true; }, "isTrue", "Not 'true'!");
	Test.add(function(value) { return value === false; },"isFalse", "Not 'false'!");
	Test.add(function(value) { return typeof value == "string"; }, "isString", "Not a string!");
	Test.add(function(value) { return typeof value == "number" && isFinite(value); }, "isNumber", "Not a number!");
	Test.add(function(value) { return parseInt(value) === value; }, "isInteger", "Not an integer!");
	Test.add(function(value) { return value != null; }, "isNotNull", "Is null!");

	Test.add(function(value, match) {
		return value == match;
	}, "isEqual", "Is not equal!");

	Test.add(function(value) {
		var type = typeof value;
		return value == null || type == "boolean" || type == "number" || type == "string";
	}, "isPrimitive", "Not a primitive value!");

	Test.add(function(value) {
		return value != null && typeof value == "object";
	}, "isObject", "Not an object!");

	// Make not use of instanceof operator as it has a memory leak in IE and also does not work cross frame.
	// Memory leak: http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak
	// Cross frame: http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

	var toStringMap = {};
	"Array Function RegExp Object".replace(/\w+/g, function(cls) {
		toStringMap[cls] = "[object " + cls + "]";
	});

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.Array;
	}, "isArray", "Not an array!");

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.Function;
	}, "isFunction", "Not a function!");

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.RegExp;
	}, "isRegExp", "Not a regular expression!");

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.Object;
	}, "isMap", "Not a map (plain object)!");

	var objectOrFunction = {
		"object" : 1,
		"function" : 1
	};

	Test.add(function(value) {
		return value != null && objectOrFunction[typeof value] == 1;
	}, "isObjectOrFunction", "Not a function or object!");

	Test.add(function(value, keys)
	{
		Test.assertMap(value);
		Test.assertArray(keys);

		var valueKeys = Object.keys(value);
		for (var i=0, l=valueKeys.length; i<l; i++)
		{
			var key = valueKeys[i];
			if (keys.indexOf(key) == -1) {
				return false;
			}
		}

		return true;
	}, "hasAllowedKeysOnly", "Defines a key %1 which is not allowed being used!");

	Test.add(function(value, regexp) {
		return typeof value == "string" && !!value.match(regexp);
	}, "matchesRegExp", "Does not match regular expression %1!");

	Test.add(function(value, list) {
		return list.indexOf(value) != -1;
	}, "isInList", "Is not in specified list!");

	Test.add(function(value, clazz) {
		// Use instanceof here, but be memory safe in IE
		return value != null && value.hasOwnProperty && value instanceof clazz;
	}, "isInstanceOf", "Is not a instance of %1!");

	Test.add(function(obj, key) {
		return obj != null && key in obj;
	}, "hasKey", "Missing key %1!");

})(this, Object.prototype.toString);
<commit_msg>Added isNative<commit_after>/*
==================================================================================================
	Jasy - JavaScript Tooling Framework
	Copyright 2010-2011 Sebastian Werner
==================================================================================================
*/

(function(global, toString, undef)
{
	if (!global.jasy) {
		global.jasy = {};
	}

	/**
	 * Test module
	 *
	 * It also bundles all fixes under "fix/" before starting with the real code.
	 *
	 * @require {fix.ArrayIndexOf}
	 * @require {fix.Console}
	 * @require {fix.DateNow}
	 * @require {fix.DocumentHead}
	 * @require {fix.ExecScript}
	 * @require {fix.FunctionBind}
	 * @require {fix.HTML5Markup}
	 * @require {fix.ObjectKeys}
	 * @require {fix.SetTimeoutArgs}
	 */
	var Test = jasy.Test =
	{
		/**
		 * Adds a new assertion check
		 *
		 * @param func {Function} Function for the test. Must return boolean.
		 * @param methodName {String} Name of the method to attach.
		 * @param assertMsg {}
		 *
		 */
		add : function(func, methodName, assertMsg)
		{
			if(!func) {
				throw new Error("Invalid function during adding assertion for " + methodName);
			}

			// Attach given method as is to assertion
			this[methodName] = func;
			if(func.displayName == null) {
				func.displayName = "jasy.Test." + methodName;
			}

			// Build assert method name
			var assertName = "assert";
			if (methodName.substring(0, 2) == "is") {
				assertName += methodName.slice(2);
			} else {
				assertName += methodName.charAt(0).toUpperCase() + methodName.slice(1);
			}

			// Wrap method throw error for simplified throwing of exceptions in type checks
			if (func.length == 1)
			{
				this[assertName] = function(value, customMsg)
				{
					if (!func(value)) {
						throw new TypeError('Value: "' + value + '": ' + (customMsg||assertMsg));
					}
				};
			}
			else
			{
				this[assertName] = function(value, test, customMsg)
				{
					if (!func(value, test))
					{
						var msg = (customMsg||assertMsg).replace("%1", ""+test);
						throw new TypeError('Value: "' + value + '": ' + msg);
					}
				};
			}

			this[assertName].displayName = "jasy.Test." + assertName;
		}
	};

	Test.add(function(value) { return typeof value == "boolean"; }, "isBoolean", "Not boolean!");
	Test.add(function(value) { return value === true; }, "isTrue", "Not 'true'!");
	Test.add(function(value) { return value === false; },"isFalse", "Not 'false'!");
	Test.add(function(value) { return typeof value == "string"; }, "isString", "Not a string!");
	Test.add(function(value) { return typeof value == "number" && isFinite(value); }, "isNumber", "Not a number!");
	Test.add(function(value) { return parseInt(value) === value; }, "isInteger", "Not an integer!");
	Test.add(function(value) { return value != null; }, "isNotNull", "Is null!");

	Test.add(function(value, match) {
		return value == match;
	}, "isEqual", "Is not equal!");

	Test.add(function(value) {
		var type = typeof value;
		return value == null || type == "boolean" || type == "number" || type == "string";
	}, "isPrimitive", "Not a primitive value!");

	Test.add(function(value) {
		return value != null && typeof value == "object";
	}, "isObject", "Not an object!");

	// Via: https://github.com/dperini/nwmatcher/blob/master/src/nwmatcher.js#L182-190
	var nativeCompare = (doc.appendChild + '').replace(/appendChild/g, '');
	Test.add(function(object, method) {
		var m = object && object[method] || false;
		return m && typeof m != 'string' &&
			nativeCompare == (m + '').replace(new RegExp(method, 'g'), '');
	}, "isNative", "Not a native method!");

	// Make not use of instanceof operator as it has a memory leak in IE and also does not work cross frame.
	// Memory leak: http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak
	// Cross frame: http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

	var toStringMap = {};
	"Array Function RegExp Object".replace(/\w+/g, function(cls) {
		toStringMap[cls] = "[object " + cls + "]";
	});

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.Array;
	}, "isArray", "Not an array!");

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.Function;
	}, "isFunction", "Not a function!");

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.RegExp;
	}, "isRegExp", "Not a regular expression!");

	Test.add(function(value) {
		return value != null && toString.call(value) == toStringMap.Object;
	}, "isMap", "Not a map (plain object)!");

	var objectOrFunction = {
		"object" : 1,
		"function" : 1
	};

	Test.add(function(value) {
		return value != null && objectOrFunction[typeof value] == 1;
	}, "isObjectOrFunction", "Not a function or object!");

	Test.add(function(value, keys)
	{
		Test.assertMap(value);
		Test.assertArray(keys);

		var valueKeys = Object.keys(value);
		for (var i=0, l=valueKeys.length; i<l; i++)
		{
			var key = valueKeys[i];
			if (keys.indexOf(key) == -1) {
				return false;
			}
		}

		return true;
	}, "hasAllowedKeysOnly", "Defines a key %1 which is not allowed being used!");

	Test.add(function(value, regexp) {
		return typeof value == "string" && !!value.match(regexp);
	}, "matchesRegExp", "Does not match regular expression %1!");

	Test.add(function(value, list) {
		return list.indexOf(value) != -1;
	}, "isInList", "Is not in specified list!");

	Test.add(function(value, clazz) {
		// Use instanceof here, but be memory safe in IE
		return value != null && value.hasOwnProperty && value instanceof clazz;
	}, "isInstanceOf", "Is not a instance of %1!");

	Test.add(function(obj, key) {
		return obj != null && key in obj;
	}, "hasKey", "Missing key %1!");

})(this, Object.prototype.toString);
<|endoftext|>


<commit_before>/**
 * @fileOverview
 * instance controls from native
 *
 * - init bundle
 *
 * corresponded with the API of instance manager (framework.js)
 */

import Vm from '../../vm/index'
import { removeWeexPrefix } from '../../util/index'
import {
  defineFn,
  bootstrap,
  register
} from '../bundle/index'
import { updateActions } from './misc'

/**
 * Init an app by run code witgh data
 * @param  {object} app
 * @param  {string} code
 * @param  {object} data
 */
export function init (app, code, data, services) {
  console.debug('[JS Framework] Intialize an instance with:\n', data)
  let result

  // prepare app env methods
  const bundleDefine = (...args) => defineFn(app, ...args)
  const bundleBootstrap = (name, config, _data) => {
    result = bootstrap(app, name, config, _data || data)
    updateActions(app)
    app.doc.listener.createFinish()
    console.debug(`[JS Framework] After intialized an instance(${app.id})`)
  }
  const bundleVm = Vm
  /* istanbul ignore next */
  const bundleRegister = (...args) => register(app, ...args)
  /* istanbul ignore next */
  const bundleRender = (name, _data) => {
    result = bootstrap(app, name, {}, _data)
  }
  /* istanbul ignore next */
  const bundleRequire = name => _data => {
    result = bootstrap(app, name, {}, _data)
  }
  const bundleDocument = app.doc
  /* istanbul ignore next */
  const bundleRequireModule = name => app.requireModule(removeWeexPrefix(name))

  const weexGlobalObject = {
    config: app.options,
    define: bundleDefine,
    bootstrap: bundleBootstrap,
    require: bundleDocument,
    document: bundleRequireModule,
    Vm: bundleVm
  }

  Object.freeze(weexGlobalObject)

  // prepare code
  let functionBody
  /* istanbul ignore if */
  if (typeof code === 'function') {
    // `function () {...}` -> `{...}`
    // not very strict
    functionBody = code.toString().substr(12)
  }
  /* istanbul ignore next */
  else if (code) {
    functionBody = code.toString()
  }
  // wrap IFFE and use strict mode
  functionBody = `(function(global){"use strict"; ${functionBody} })(Object.create(this))`

  // run code and get result
  const { WXEnvironment } = global
  const timerAPIs = {}
  /* istanbul ignore if */
  if (WXEnvironment && WXEnvironment.platform !== 'Web') {
    // timer APIs polyfill in native
    const timer = app.requireModule('timer')
    Object.assign(timerAPIs, {
      setTimeout: (...args) => {
        const handler = function () {
          args[0](...args.slice(2))
        }
        timer.setTimeout(handler, args[1])
        return app.uid.toString()
      },
      setInterval: (...args) => {
        const handler = function () {
          args[0](...args.slice(2))
        }
        timer.setInterval(handler, args[1])
        return app.uid.toString()
      },
      clearTimeout: (n) => {
        timer.clearTimeout(n)
      },
      clearInterval: (n) => {
        timer.clearInterval(n)
      }
    })
  }
  // run code and get result
  const globalObjects = Object.assign({
    define: bundleDefine,
    require: bundleRequire,
    bootstrap: bundleBootstrap,
    register: bundleRegister,
    render: bundleRender,
    __weex_define__: bundleDefine, // alias for define
    __weex_bootstrap__: bundleBootstrap, // alias for bootstrap
    __weex_document__: bundleDocument,
    __weex_require__: bundleRequireModule,
    __weex_viewmodel__: bundleVm,
    weex: weexGlobalObject
  }, timerAPIs, services)
  callFunction(globalObjects, functionBody)

  return result
}

/**
 * Call a new function body with some global objects.
 * @param  {object} globalObjects
 * @param  {string} code
 * @return {any}
 */
function callFunction (globalObjects, body) {
  const globalKeys = []
  const globalValues = []
  for (const key in globalObjects) {
    globalKeys.push(key)
    globalValues.push(globalObjects[key])
  }
  globalKeys.push(body)

  const result = new Function(...globalKeys)
  return result(...globalValues)
}
<commit_msg>* [jsfm] insert blank line in js bundle function body<commit_after>/**
 * @fileOverview
 * instance controls from native
 *
 * - init bundle
 *
 * corresponded with the API of instance manager (framework.js)
 */

import Vm from '../../vm/index'
import { removeWeexPrefix } from '../../util/index'
import {
  defineFn,
  bootstrap,
  register
} from '../bundle/index'
import { updateActions } from './misc'

/**
 * Init an app by run code witgh data
 * @param  {object} app
 * @param  {string} code
 * @param  {object} data
 */
export function init (app, code, data, services) {
  console.debug('[JS Framework] Intialize an instance with:\n', data)
  let result

  // prepare app env methods
  const bundleDefine = (...args) => defineFn(app, ...args)
  const bundleBootstrap = (name, config, _data) => {
    result = bootstrap(app, name, config, _data || data)
    updateActions(app)
    app.doc.listener.createFinish()
    console.debug(`[JS Framework] After intialized an instance(${app.id})`)
  }
  const bundleVm = Vm
  /* istanbul ignore next */
  const bundleRegister = (...args) => register(app, ...args)
  /* istanbul ignore next */
  const bundleRender = (name, _data) => {
    result = bootstrap(app, name, {}, _data)
  }
  /* istanbul ignore next */
  const bundleRequire = name => _data => {
    result = bootstrap(app, name, {}, _data)
  }
  const bundleDocument = app.doc
  /* istanbul ignore next */
  const bundleRequireModule = name => app.requireModule(removeWeexPrefix(name))

  const weexGlobalObject = {
    config: app.options,
    define: bundleDefine,
    bootstrap: bundleBootstrap,
    require: bundleDocument,
    document: bundleRequireModule,
    Vm: bundleVm
  }

  Object.freeze(weexGlobalObject)

  // prepare code
  let functionBody
  /* istanbul ignore if */
  if (typeof code === 'function') {
    // `function () {...}` -> `{...}`
    // not very strict
    functionBody = code.toString().substr(12)
  }
  /* istanbul ignore next */
  else if (code) {
    functionBody = code.toString()
  }
  // wrap IFFE and use strict mode
  functionBody = `(function(global){\n\n"use strict";\n\n ${functionBody} \n\n})(Object.create(this))`

  // run code and get result
  const { WXEnvironment } = global
  const timerAPIs = {}
  /* istanbul ignore if */
  if (WXEnvironment && WXEnvironment.platform !== 'Web') {
    // timer APIs polyfill in native
    const timer = app.requireModule('timer')
    Object.assign(timerAPIs, {
      setTimeout: (...args) => {
        const handler = function () {
          args[0](...args.slice(2))
        }
        timer.setTimeout(handler, args[1])
        return app.uid.toString()
      },
      setInterval: (...args) => {
        const handler = function () {
          args[0](...args.slice(2))
        }
        timer.setInterval(handler, args[1])
        return app.uid.toString()
      },
      clearTimeout: (n) => {
        timer.clearTimeout(n)
      },
      clearInterval: (n) => {
        timer.clearInterval(n)
      }
    })
  }
  // run code and get result
  const globalObjects = Object.assign({
    define: bundleDefine,
    require: bundleRequire,
    bootstrap: bundleBootstrap,
    register: bundleRegister,
    render: bundleRender,
    __weex_define__: bundleDefine, // alias for define
    __weex_bootstrap__: bundleBootstrap, // alias for bootstrap
    __weex_document__: bundleDocument,
    __weex_require__: bundleRequireModule,
    __weex_viewmodel__: bundleVm,
    weex: weexGlobalObject
  }, timerAPIs, services)
  callFunction(globalObjects, functionBody)

  return result
}

/**
 * Call a new function body with some global objects.
 * @param  {object} globalObjects
 * @param  {string} code
 * @return {any}
 */
function callFunction (globalObjects, body) {
  const globalKeys = []
  const globalValues = []
  for (const key in globalObjects) {
    globalKeys.push(key)
    globalValues.push(globalObjects[key])
  }
  globalKeys.push(body)

  const result = new Function(...globalKeys)
  return result(...globalValues)
}
<|endoftext|>


<commit_before>angular.module('ionicApp', ['ionic'])

  .config(function($stateProvider, $urlRouterProvider) {

    $stateProvider
      .state('eventmenu', {
        url: "/event",
        abstract: true,
        templateUrl: "templates/event-menu.html"
      })
      .state('eventmenu.shirtsize', {
        url: "/shirtsize",
        views: {
          'menuContent': {
            templateUrl: "templates/shirtsize.html",
            controller: "ShirtSizeCtrl"
          }
        }
      })
      .state('eventmenu.checkin', {
        url: "/check-in",
        views: {
          'menuContent': {
            templateUrl: "templates/check-in.html",
            controller: "CheckinCtrl"
          }
        }
      })

    $urlRouterProvider.otherwise("/event/check-in");
  })

  .controller('CheckinCtrl', function($scope, $state, myshirt) {

    $scope.data = {shirtSize: myshirt.size};

    //$scope.refreshData = function() {
    //	  $scope.data = {shirtSize: myshirt.size};
    //}

  })

  .controller('ShirtSizeCtrl', function($scope, $state, myshirt) {

    $scope.shirtSizeList = [
      {text: 'Large',value: 'L'},
      {text: 'Medium',value: 'M'},
      {text: 'Small',value: 'S'}];

    $scope.data = {shirtSize: myshirt.size};

    $scope.sizechanged = function(item) {
      myshirt.updateSize(item.value);
      $state.go('eventmenu.checkin');
    };

  })

  .service("myshirt", function MyShirt() {
    var myshirt = this;
    myshirt.size = 'S';
    myshirt.updateSize = function(value){
      this.size = value;
    }
  })
;
<commit_msg>删除临时文件<commit_after><|endoftext|>


<commit_before>'use strict';
var math = require('mathjs'),
    async = require('async');
var errors;

exports.name = 'Math';
exports.version = '0.1.3';
exports.description = 'Do mathematics!';
exports.configuration = {
    enabled: false
};

exports.onCommand = function onCommand(_, command, args, __, callback) {
    if (command.toLowerCase() !== 'calc'){
        args.unshift(command);
    }
    calc(args, callback);
};

exports.begin = function begin(_, config) {
    errors = config.errors;
    math.config({
        number: 'bignumber',
        precision: 4096
    });
};

function calc(args, callback) {
    async.series([function (cb) {
            var realExpression = args.join(' ');
            try {
                var result = math.eval(realExpression);
                var message = [
                    'Expression: ',
                    realExpression.trim(),
                    '\nResult: ',
                    result,
                    ''
                ];
                cb(null, message.join('\n'));
            } catch (e) {
                var error = [
                    'Unable to evaluate expression ' + realExpression,
                    errors[Math.floor(Math.random() * errors.length)],
                    ''
                ];
                cb(null, error.join('\n'));
            }
        }
    ],
    function (err, results) {
        if (err) {
            callback(err, null);
        } else {
            callback(null, results[0]);
        }
    });
}
<commit_msg>Upping version<commit_after>'use strict';
var math = require('mathjs'),
    async = require('async');
var errors;

exports.name = 'Math';
exports.version = '0.2.0';
exports.description = 'Do mathematics!';
exports.configuration = {
    enabled: false
};

exports.onCommand = function onCommand(_, command, args, __, callback) {
    if (command.toLowerCase() !== 'calc'){
        args.unshift(command);
    }
    calc(args, callback);
};

exports.begin = function begin(_, config) {
    errors = config.errors;
    math.config({
        number: 'bignumber',
        precision: 4096
    });
};

function calc(args, callback) {
    async.series([function (cb) {
            var realExpression = args.join(' ');
            try {
                var result = math.eval(realExpression);
                var message = [
                    'Expression: ',
                    realExpression.trim(),
                    '\nResult: ',
                    result,
                    ''
                ];
                cb(null, message.join('\n'));
            } catch (e) {
                var error = [
                    'Unable to evaluate expression ' + realExpression,
                    errors[Math.floor(Math.random() * errors.length)],
                    ''
                ];
                cb(null, error.join('\n'));
            }
        }
    ],
    function (err, results) {
        if (err) {
            callback(err, null);
        } else {
            callback(null, results[0]);
        }
    });
}
<|endoftext|>


<commit_before>/* global YT */
angular.module('youtube-embed', [])
.service ('youtubeEmbedUtils', ['$window', '$rootScope', function ($window, $rootScope) {
    var Service = {}

    // adapted from http://stackoverflow.com/a/5831191/1614967
    var youtubeRegexp = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube(?:-nocookie)?\.com\S*[^\w\s-])([\w-]{11})(?=[^\w-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig;
    var timeRegexp = /t=(\d+)[ms]?(\d+)?s?/;

    function contains(str, substr) {
        return (str.indexOf(substr) > -1);
    }

    Service.getIdFromURL = function getIdFromURL(url) {
        var id = url.replace(youtubeRegexp, '$1');

        if (contains(id, ';')) {
            var pieces = id.split(';');

            if (contains(pieces[1], '%')) {
                // links like this:
                // "http://www.youtube.com/attribution_link?a=pxa6goHqzaA&u=%2Fwatch%3Fv%3DdPdgx30w9sU%26feature%3Dshare"
                // have the real query string URI encoded behind a ';'.
                // at this point, `id is 'pxa6goHqzaA;u=%2Fwatch%3Fv%3DdPdgx30w9sU%26feature%3Dshare'
                var uriComponent = decodeURIComponent(pieces[1]);
                id = ('http://youtube.com' + uriComponent)
                        .replace(youtubeRegexp, '$1');
            } else {
                // https://www.youtube.com/watch?v=VbNF9X1waSc&feature=youtu.be
                // `id` looks like 'VbNF9X1waSc;feature=youtu.be' currently.
                // strip the ';feature=youtu.be'
                id = pieces[0];
            }
        } else if (contains(id, '#')) {
            // id might look like '93LvTKF_jW0#t=1'
            // and we want '93LvTKF_jW0'
            id = id.split('#')[0];
        }

        return id;
    };

    Service.getTimeFromURL = function getTimeFromURL(url) {
        url = url || '';

        // t=4m20s
        // returns ['t=4m20s', '4', '20']
        // t=46s
        // returns ['t=46s', '46']
        // t=46
        // returns ['t=46', '46']
        var times = url.match(timeRegexp);

        if (!times) {
            // zero seconds
            return 0;
        }

        // assume the first
        var full = times[0],
            minutes = times[1],
            seconds = times[2];

        // t=4m20s
        if (typeof seconds !== 'undefined') {
            seconds = parseInt(seconds, 10);
            minutes = parseInt(minutes, 10);

        // t=4m
        } else if (contains(full, 'm')) {
            minutes = parseInt(minutes, 10);
            seconds = 0;

        // t=4s
        // t=4
        } else {
            seconds = parseInt(minutes, 10);
            minutes = 0;
        }

        // in seconds
        return seconds + (minutes * 60);
    };

    Service.ready = false;

    function applyServiceIsReady() {
        $rootScope.$apply(function () {
            Service.ready = true;
        });
    };

    // If the library isn't here at all,
    if (typeof YT === "undefined") {
        // ...grab on to global callback, in case it's eventually loaded
        $window.onYouTubeIframeAPIReady = applyServiceIsReady;
    } else if (YT.loaded) {
        Service.ready = true;
    } else {
        YT.ready(applyServiceIsReady);
    }

    return Service;
}])
.directive('youtubeVideo', ['$window', 'youtubeEmbedUtils', function ($window, youtubeEmbedUtils) {
    var uniqId = 1;

    // from YT.PlayerState
    var stateNames = {
        '-1': 'unstarted',
        0: 'ended',
        1: 'playing',
        2: 'paused',
        3: 'buffering',
        5: 'queued'
    };

    var eventPrefix = 'youtube.player.';

    $window.YTConfig = {
        host: 'https://www.youtube.com'
    };

    return {
        restrict: 'EA',
        scope: {
            videoId: '=?',
            videoUrl: '=?',
            player: '=?',
            playerVars: '=?',
            playerHeight: '=?',
            playerWidth: '=?'
        },
        link: function (scope, element, attrs) {
            // allows us to $watch `ready`
            scope.utils = youtubeEmbedUtils;

            // player-id attr > id attr > directive-generated ID
            var playerId = attrs.playerId || element[0].id || 'unique-youtube-embed-id-' + uniqId++;
            element[0].id = playerId;

            // Attach to element
            scope.playerHeight = scope.playerHeight || 390;
            scope.playerWidth = scope.playerWidth || 640;
            scope.playerVars = scope.playerVars || {};

            // YT calls callbacks outside of digest cycle
            function applyBroadcast () {
                var args = Array.prototype.slice.call(arguments);
                scope.$apply(function () {
                    scope.$emit.apply(scope, args);
                });
            }

            function onPlayerStateChange (event) {
                var state = stateNames[event.data];
                if (typeof state !== 'undefined') {
                    applyBroadcast(eventPrefix + state, scope.player, event);
                }
                scope.$apply(function () {
                    scope.player.currentState = state;
                });
            }

            function onPlayerReady (event) {
                applyBroadcast(eventPrefix + 'ready', scope.player, event);
            }

            function onPlayerError (event) {
                applyBroadcast(eventPrefix + 'error', scope.player, event);
            }

            function createPlayer () {
                var playerVars = angular.copy(scope.playerVars);
                playerVars.start = playerVars.start || scope.urlStartTime;
                var player = new YT.Player(playerId, {
                    height: scope.playerHeight,
                    width: scope.playerWidth,
                    videoId: scope.videoId,
                    playerVars: playerVars,
                    events: {
                        onReady: onPlayerReady,
                        onStateChange: onPlayerStateChange,
                        onError: onPlayerError
                    }
                });

                player.id = playerId;
                return player;
            }

            function loadPlayer () {
                if (scope.videoId || scope.playerVars.list) {
                    if (scope.player && typeof scope.player.destroy === 'function') {
                        scope.player.destroy();
                    }

                    scope.player = createPlayer();
                }
            };

            var stopWatchingReady = scope.$watch(
                function () {
                    return scope.utils.ready
                        // Wait until one of them is defined...
                        && (typeof scope.videoUrl !== 'undefined'
                        ||  typeof scope.videoId !== 'undefined'
                        ||  typeof scope.playerVars.list !== 'undefined');
                },
                function (ready) {
                    if (ready) {
                        stopWatchingReady();

                        // URL takes first priority
                        if (typeof scope.videoUrl !== 'undefined') {
                            scope.$watch('videoUrl', function (url) {
                                scope.videoId = scope.utils.getIdFromURL(url);
                                scope.urlStartTime = scope.utils.getTimeFromURL(url);

                                loadPlayer();
                            });

                        // then, a video ID
                        } else if (typeof scope.videoId !== 'undefined') {
                            scope.$watch('videoId', function () {
                                scope.urlStartTime = null;
                                loadPlayer();
                            });

                        // finally, a list
                        } else {
                            scope.$watch('playerVars.list', function () {
                                scope.urlStartTime = null;
                                loadPlayer();
                            });
                        }
                    }
            });

            scope.$watchCollection(['playerHeight', 'playerWidth'], function() {
                if (scope.player) {
                    scope.player.setSize(scope.playerWidth, scope.playerHeight);
                }
            });

            scope.$on('$destroy', function () {
                scope.player && scope.player.destroy();
            });
        }
    };
}]);
<commit_msg>add console.log if YouTube library is not found, fix #122<commit_after>/* global YT */
angular.module('youtube-embed', [])
.service ('youtubeEmbedUtils', ['$window', '$rootScope', function ($window, $rootScope) {
    var Service = {}

    // adapted from http://stackoverflow.com/a/5831191/1614967
    var youtubeRegexp = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube(?:-nocookie)?\.com\S*[^\w\s-])([\w-]{11})(?=[^\w-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig;
    var timeRegexp = /t=(\d+)[ms]?(\d+)?s?/;

    function contains(str, substr) {
        return (str.indexOf(substr) > -1);
    }

    Service.getIdFromURL = function getIdFromURL(url) {
        var id = url.replace(youtubeRegexp, '$1');

        if (contains(id, ';')) {
            var pieces = id.split(';');

            if (contains(pieces[1], '%')) {
                // links like this:
                // "http://www.youtube.com/attribution_link?a=pxa6goHqzaA&u=%2Fwatch%3Fv%3DdPdgx30w9sU%26feature%3Dshare"
                // have the real query string URI encoded behind a ';'.
                // at this point, `id is 'pxa6goHqzaA;u=%2Fwatch%3Fv%3DdPdgx30w9sU%26feature%3Dshare'
                var uriComponent = decodeURIComponent(pieces[1]);
                id = ('http://youtube.com' + uriComponent)
                        .replace(youtubeRegexp, '$1');
            } else {
                // https://www.youtube.com/watch?v=VbNF9X1waSc&feature=youtu.be
                // `id` looks like 'VbNF9X1waSc;feature=youtu.be' currently.
                // strip the ';feature=youtu.be'
                id = pieces[0];
            }
        } else if (contains(id, '#')) {
            // id might look like '93LvTKF_jW0#t=1'
            // and we want '93LvTKF_jW0'
            id = id.split('#')[0];
        }

        return id;
    };

    Service.getTimeFromURL = function getTimeFromURL(url) {
        url = url || '';

        // t=4m20s
        // returns ['t=4m20s', '4', '20']
        // t=46s
        // returns ['t=46s', '46']
        // t=46
        // returns ['t=46', '46']
        var times = url.match(timeRegexp);

        if (!times) {
            // zero seconds
            return 0;
        }

        // assume the first
        var full = times[0],
            minutes = times[1],
            seconds = times[2];

        // t=4m20s
        if (typeof seconds !== 'undefined') {
            seconds = parseInt(seconds, 10);
            minutes = parseInt(minutes, 10);

        // t=4m
        } else if (contains(full, 'm')) {
            minutes = parseInt(minutes, 10);
            seconds = 0;

        // t=4s
        // t=4
        } else {
            seconds = parseInt(minutes, 10);
            minutes = 0;
        }

        // in seconds
        return seconds + (minutes * 60);
    };

    Service.ready = false;

    function applyServiceIsReady() {
        $rootScope.$apply(function () {
            Service.ready = true;
        });
    };

    // If the library isn't here at all,
    if (typeof YT === "undefined") {
        // ...grab on to global callback, in case it's eventually loaded
        $window.onYouTubeIframeAPIReady = applyServiceIsReady;
        console.log('Unable to find YouTube iframe library on this page.')
    } else if (YT.loaded) {
        Service.ready = true;
    } else {
        YT.ready(applyServiceIsReady);
    }

    return Service;
}])
.directive('youtubeVideo', ['$window', 'youtubeEmbedUtils', function ($window, youtubeEmbedUtils) {
    var uniqId = 1;

    // from YT.PlayerState
    var stateNames = {
        '-1': 'unstarted',
        0: 'ended',
        1: 'playing',
        2: 'paused',
        3: 'buffering',
        5: 'queued'
    };

    var eventPrefix = 'youtube.player.';

    $window.YTConfig = {
        host: 'https://www.youtube.com'
    };

    return {
        restrict: 'EA',
        scope: {
            videoId: '=?',
            videoUrl: '=?',
            player: '=?',
            playerVars: '=?',
            playerHeight: '=?',
            playerWidth: '=?'
        },
        link: function (scope, element, attrs) {
            // allows us to $watch `ready`
            scope.utils = youtubeEmbedUtils;

            // player-id attr > id attr > directive-generated ID
            var playerId = attrs.playerId || element[0].id || 'unique-youtube-embed-id-' + uniqId++;
            element[0].id = playerId;

            // Attach to element
            scope.playerHeight = scope.playerHeight || 390;
            scope.playerWidth = scope.playerWidth || 640;
            scope.playerVars = scope.playerVars || {};

            // YT calls callbacks outside of digest cycle
            function applyBroadcast () {
                var args = Array.prototype.slice.call(arguments);
                scope.$apply(function () {
                    scope.$emit.apply(scope, args);
                });
            }

            function onPlayerStateChange (event) {
                var state = stateNames[event.data];
                if (typeof state !== 'undefined') {
                    applyBroadcast(eventPrefix + state, scope.player, event);
                }
                scope.$apply(function () {
                    scope.player.currentState = state;
                });
            }

            function onPlayerReady (event) {
                applyBroadcast(eventPrefix + 'ready', scope.player, event);
            }

            function onPlayerError (event) {
                applyBroadcast(eventPrefix + 'error', scope.player, event);
            }

            function createPlayer () {
                var playerVars = angular.copy(scope.playerVars);
                playerVars.start = playerVars.start || scope.urlStartTime;
                var player = new YT.Player(playerId, {
                    height: scope.playerHeight,
                    width: scope.playerWidth,
                    videoId: scope.videoId,
                    playerVars: playerVars,
                    events: {
                        onReady: onPlayerReady,
                        onStateChange: onPlayerStateChange,
                        onError: onPlayerError
                    }
                });

                player.id = playerId;
                return player;
            }

            function loadPlayer () {
                if (scope.videoId || scope.playerVars.list) {
                    if (scope.player && typeof scope.player.destroy === 'function') {
                        scope.player.destroy();
                    }

                    scope.player = createPlayer();
                }
            };

            var stopWatchingReady = scope.$watch(
                function () {
                    return scope.utils.ready
                        // Wait until one of them is defined...
                        && (typeof scope.videoUrl !== 'undefined'
                        ||  typeof scope.videoId !== 'undefined'
                        ||  typeof scope.playerVars.list !== 'undefined');
                },
                function (ready) {
                    if (ready) {
                        stopWatchingReady();

                        // URL takes first priority
                        if (typeof scope.videoUrl !== 'undefined') {
                            scope.$watch('videoUrl', function (url) {
                                scope.videoId = scope.utils.getIdFromURL(url);
                                scope.urlStartTime = scope.utils.getTimeFromURL(url);

                                loadPlayer();
                            });

                        // then, a video ID
                        } else if (typeof scope.videoId !== 'undefined') {
                            scope.$watch('videoId', function () {
                                scope.urlStartTime = null;
                                loadPlayer();
                            });

                        // finally, a list
                        } else {
                            scope.$watch('playerVars.list', function () {
                                scope.urlStartTime = null;
                                loadPlayer();
                            });
                        }
                    }
            });

            scope.$watchCollection(['playerHeight', 'playerWidth'], function() {
                if (scope.player) {
                    scope.player.setSize(scope.playerWidth, scope.playerHeight);
                }
            });

            scope.$on('$destroy', function () {
                scope.player && scope.player.destroy();
            });
        }
    };
}]);
<|endoftext|>