Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- var requirejs, require, define;
- ! function(a) {
- function b(a, b) {
- return s.call(a, b)
- }
- function c(a, b) {
- var c, d, e, f, g, h, i, j, k, l, m, n, o = b && b.split("/"),
- p = q.map,
- r = p && p["*"] || {};
- if (a) {
- for (a = a.split("/"), g = a.length - 1, q.nodeIdCompat && u.test(a[g]) && (a[g] = a[g].replace(u, "")), "." === a[0].charAt(0) && o && (n = o.slice(0, o.length - 1), a = n.concat(a)), k = 0; k < a.length; k++)
- if (m = a[k], "." === m) a.splice(k, 1), k -= 1;
- else if (".." === m) {
- if (0 === k || 1 === k && ".." === a[2] || ".." === a[k - 1]) continue;
- k > 0 && (a.splice(k - 1, 2), k -= 2)
- }
- a = a.join("/")
- }
- if ((o || r) && p) {
- for (c = a.split("/"), k = c.length; k > 0; k -= 1) {
- if (d = c.slice(0, k).join("/"), o)
- for (l = o.length; l > 0; l -= 1)
- if (e = p[o.slice(0, l).join("/")], e && (e = e[d])) {
- f = e, h = k;
- break
- }
- if (f) break;
- !i && r && r[d] && (i = r[d], j = k)
- }!f && i && (f = i, h = j), f && (c.splice(0, h, f), a = c.join("/"))
- }
- return a
- }
- function d(b, c) {
- return function() {
- var d = t.call(arguments, 0);
- return "string" != typeof d[0] && 1 === d.length && d.push(null), l.apply(a, d.concat([b, c]))
- }
- }
- function e(a) {
- return function(b) {
- return c(b, a)
- }
- }
- function f(a) {
- return function(b) {
- o[a] = b
- }
- }
- function g(c) {
- if (b(p, c)) {
- var d = p[c];
- delete p[c], r[c] = !0, k.apply(a, d)
- }
- if (!b(o, c) && !b(r, c)) throw new Error("No " + c);
- return o[c]
- }
- function h(a) {
- var b, c = a ? a.indexOf("!") : -1;
- return c > -1 && (b = a.substring(0, c), a = a.substring(c + 1, a.length)), [b, a]
- }
- function i(a) {
- return a ? h(a) : []
- }
- function j(a) {
- return function() {
- return q && q.config && q.config[a] || {}
- }
- }
- var k, l, m, n, o = {},
- p = {},
- q = {},
- r = {},
- s = Object.prototype.hasOwnProperty,
- t = [].slice,
- u = /\.js$/;
- m = function(a, b) {
- var d, f = h(a),
- i = f[0],
- j = b[1];
- return a = f[1], i && (i = c(i, j), d = g(i)), i ? a = d && d.normalize ? d.normalize(a, e(j)) : c(a, j) : (a = c(a, j), f = h(a), i = f[0], a = f[1], i && (d = g(i))), {
- f: i ? i + "!" + a : a,
- n: a,
- pr: i,
- p: d
- }
- }, n = {
- require: function(a) {
- return d(a)
- },
- exports: function(a) {
- var b = o[a];
- return "undefined" != typeof b ? b : o[a] = {}
- },
- module: function(a) {
- return {
- id: a,
- uri: "",
- exports: o[a],
- config: j(a)
- }
- }
- }, k = function(c, e, h, j) {
- var k, l, q, s, t, u, v, w = [],
- x = typeof h;
- if (j = j || c, u = i(j), "undefined" === x || "function" === x) {
- for (e = !e.length && h.length ? ["require", "exports", "module"] : e, t = 0; t < e.length; t += 1)
- if (s = m(e[t], u), l = s.f, "require" === l) w[t] = n.require(c);
- else if ("exports" === l) w[t] = n.exports(c), v = !0;
- else if ("module" === l) k = w[t] = n.module(c);
- else if (b(o, l) || b(p, l) || b(r, l)) w[t] = g(l);
- else {
- if (!s.p) throw new Error(c + " missing " + l);
- s.p.load(s.n, d(j, !0), f(l), {}), w[t] = o[l]
- }
- q = h ? h.apply(o[c], w) : void 0, c && (k && k.exports !== a && k.exports !== o[c] ? o[c] = k.exports : q === a && v || (o[c] = q))
- } else c && (o[c] = h)
- }, requirejs = require = l = function(b, c, d, e, f) {
- if ("string" == typeof b) return n[b] ? n[b](c) : g(m(b, i(c)).f);
- if (!b.splice) {
- if (q = b, q.deps && l(q.deps, q.callback), !c) return;
- c.splice ? (b = c, c = d, d = null) : b = a
- }
- return c = c || function() {}, "function" == typeof d && (d = e, e = f), e ? k(a, b, c, d) : setTimeout(function() {
- k(a, b, c, d)
- }, 4), l
- }, l.config = function(a) {
- return l(a)
- }, requirejs._defined = o, define = function(a, c, d) {
- if ("string" != typeof a) throw new Error("See almond README: incorrect module build, no module name");
- c.splice || (d = c, c = []), b(o, a) || b(p, a) || (p[a] = [a, c, d])
- }, define.amd = {
- jQuery: !0
- }
- }(), define("../../../bower_components/almond/almond", function() {}), define("templates", ["require"], function(a) {
- "use strict";
- return function(a, b) {
- return window.FTWTEMPLATES[a + "_" + b] ? window.FTWTEMPLATES[a + "_" + b] : (console.error("Unable to find template: " + a + "_" + b), function() {
- return "Unable to find template: " + a + "_" + b
- })
- }
- }), define("registry", ["require"], function(a) {
- "use strict";
- var b = [];
- return {
- get: function(a) {
- return b[a] || void 0
- },
- set: function(a, c) {
- b[a] = c
- }
- }
- }), define("shared/scoring", ["require"], function(a) {
- "use strict";
- return {
- speed: function(a, b, c, d) {
- "object" == typeof a && (b = a.seconds, c = a.errors, d = a.speedType, a = a.typed), d = d || "wpm", c = c || 0;
- var e, f, g = b / 60;
- return "kph" === d ? f = Math.round(a / b * 3600) : "cpm" === d ? (e = a, f = Math.max(Math.round(e / g), 0)) : (e = a / 5, f = Math.max(Math.round(e / g), 0)), f === 1 / 0 || isNaN(f) ? 0 : f
- },
- accuracy: function(a, b) {
- "object" == typeof a && (b = a.errors, a = a.typed);
- var c;
- return a = a || 1, c = 100 - Math.round(b / a * 100), isNaN(c) ? 0 : c
- },
- stars: function(a, b, c) {
- return b = b || 90, c = c || 95, b > a ? 1 : c > a ? 2 : 3
- }
- }
- }), define("global/views/confirm", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- className: "popup-wrap",
- events: {
- "click .close": "cancel",
- "click .ok": "ok"
- },
- initialize: function(a) {
- this.options = a || {}, this.options.position = "fixed", this.template = b("global", "confirm"), this.render()
- },
- render: function() {
- return this.$el.html(this.template({
- title: this.options.title,
- text: this.options.text,
- ok: this.options.ok || "OK",
- cancel: this.options.cancel || "Cancel"
- })).fastHide(), $("body").append(this.el), this
- },
- cancel: function() {
- return this.trigger("close", !1), this.close(), !1
- },
- ok: function() {
- return this.trigger("close", !0), this.options.doNotClose ? this.$(".ok").addClass("pending") : this.close(), !1
- },
- close: function() {
- return this.$el.velocity("fadeOut", this.remove.bind(this)), !1
- },
- show: function() {
- return this.render(), this.$el.velocity("fadeIn"), this
- }
- })
- }), define("global/collections/badges", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates"),
- c = Backbone.Model.extend({});
- return Backbone.Collection.extend({
- model: c,
- totalBadges: 12,
- initialize: function() {
- for (var a = [], c = 1; c <= this.totalBadges; c++) a.push({
- id: c,
- template: b("badges", "badge" + c)
- });
- this.reset(a)
- }
- })
- }), define("global/models/lesson", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "lesson_id",
- defaults: {
- name: "",
- course: "",
- intro: "",
- seconds: 0,
- typed: 0,
- errors: 0,
- congrats: "",
- progress: 0,
- max_progress: 0
- }
- });
- return b
- }), define("global/collections/lessons", ["require", "global/models/lesson", "shared/scoring"], function(a) {
- "use strict";
- var b = a("global/models/lesson"),
- c = a("shared/scoring"),
- d = Backbone.Collection.extend({
- model: b,
- comparator: "sort_order",
- setProgress: function(a) {
- a.forEach(function(a) {
- var b = this.get(a.id);
- b && "test" != b.get("course") && "problemkeys" != b.get("course") && this.get(a.id).set({
- progress: a.get("progress"),
- max_progress: Math.max(a.get("progress"), a.get("max_progress")),
- speed: c.speed(a.get("typed"), a.get("seconds"), a.get("errors")),
- accuracy: c.accuracy(a.get("typed"), a.get("errors")),
- stars: a.get("stars"),
- updated_at: a.get("updated_at")
- })
- }.bind(this))
- }
- });
- return d
- }), define("index/views/dashboard", ["require", "templates", "shared/scoring", "registry", "global/views/confirm", "global/collections/badges", "global/collections/lessons"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/scoring"),
- d = a("registry"),
- e = a("global/views/confirm"),
- f = a("global/collections/badges"),
- g = a("global/collections/lessons");
- return Backbone.View.extend({
- daysBack: 1,
- events: {
- "click .clear": "clearProblemKeys",
- "click .filter span": "changeTime",
- "mouseenter .badge": "hoverBadge",
- "mouseleave .badge": "hoverBadge"
- },
- initialize: function(a) {
- this.options = a, void 0 === this.template && (this.template = b("index", "dashboard")), this.userBadges = d.get("userBadges"), this.user = d.get("user"), this.userLettersTyped = d.get("userLettersTyped"), this.userActivity = d.get("userActivity"), this.lessons = new g(FTWGLOBALS("lessons")), this.badges = new f, this.render()
- },
- render: function() {
- var a = this.userActivity.getCompiled(this.daysBack),
- b = this.userBadges.last(),
- d = this.userLettersTyped.topProblemKeys(3).map(function(a) {
- return a.id.toUpperCase()
- }),
- e = this.badges.get(1).get("template");
- if (b) {
- b = b.toJSON();
- var f = this.lessons.get(b.lesson_id);
- b.lesson = f.get("name"), b.speed = c.speed(b), b.accuracy = c.accuracy(b), b.course = f.get("course"), e = this.badges.get(f.get("badge") || 1).get("template")
- }
- var g = d.join("");
- return "FUK" == g && (g = "UKF"), this.$el.html(this.template({
- zeroState: 0 === this.userActivity.length && FTWGLOBALS("loggedIn"),
- daysBack: this.daysBack,
- user: this.user.toJSON(),
- speed: c.speed(a.typed, a.seconds, a.errors),
- accuracy: c.accuracy(a.typed, a.errors),
- time: a.seconds,
- keys: g,
- loggedIn: FTWGLOBALS("loggedIn"),
- badge: b,
- badgeTemplate: e
- })), this
- },
- changeTime: function(a) {
- this.daysBack = $(a.currentTarget).data("id");
- var b = this.userActivity.getCompiled(this.daysBack);
- this.$("#dashboard-speed").countdown(c.speed(b.typed, b.seconds, b.errors), null, 400).parent().velocity("fadeIn"), this.$("#dashboard-accuracy").countdown(c.accuracy(b.typed, b.errors), null, 400).parent().velocity("fadeIn");
- var d = this.$("#dashboard-time"),
- e = d.data("data");
- return d.data("data", b.seconds), d.countdown([b.seconds, e], {
- formatter: function(a) {
- return a.countdownSeconds()
- }
- }, 400).parent().velocity("fadeIn"), this.$(".filter span").removeClass("active"), this.$(".filter span[data-id=" + this.daysBack + "]").addClass("active"), !1
- },
- clearProblemKeys: function() {
- return new e({
- title: "Clear Your Problem Keys?",
- text: "Are you sure you would like to clear your Problem Keys?<br /><br />Problem Keys will begin tracking again moving forward."
- }).show().once("close", function(a) {
- a && (this.userLettersTyped.clear(), this.render())
- }.bind(this)), !1
- },
- hoverBadge: function(a) {
- "mouseenter" == a.type ? $(a.currentTarget).velocity("stop", !0).velocity({
- scale: [1.2, 1]
- }, 100) : $(a.currentTarget).velocity("stop", !0).velocity("reverse", 50)
- }
- })
- }), define("index/views/migration", ["require", "templates", "shared/scoring", "registry"], function(a) {
- "use strict";
- var b = a("templates"),
- c = (a("shared/scoring"), a("registry"));
- return Backbone.View.extend({
- events: {
- "click .close-button": "hide"
- },
- initialize: function(a) {
- this.options = a, void 0 === this.template && (this.template = b("index", "migration")), this.user = c.get("user"), this.render()
- },
- render: function() {
- return this.$el.html(this.template({
- teacherId: this.user.get("teacher_id")
- })), this
- },
- hide: function() {
- var a = this.$("input[type=checkbox]");
- a.prop("checked") && $.post("/apiv1/student/user/account", {
- migrated: 2
- }), this.user.set({
- migrated: 0
- }), this.$el.velocity("slideUp")
- }
- })
- }), define("global/collections/courses", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "course",
- defaults: {
- course: "",
- sort_order: 0
- }
- });
- return Backbone.Collection.extend({
- model: b,
- sort: "sort_order",
- setProgress: function(a) {
- var b, c, d = a.toJSON();
- this.forEach(function(a) {
- if ("test" != a.get("type") && "problemkeys" != a.get("type")) {
- c = _.where(d, {
- course: a.id
- });
- var e = c.reduce(function(a, b) {
- return a + b.max_progress
- }, 0);
- if (e > 0) {
- var f = _.indexOf(c, _.max(c, function(a) {
- return a.max_progress == a.screens ? a.updated_at : 0
- })),
- g = _.max(c, function(a) {
- return a.max_progress == a.screens ? 0 : a.updated_at
- }),
- h = _.max(c, function(a) {
- return a.updated_at
- });
- if (g.max_progress == g.screens && (g = null), -1 == f) b = _.max(c, function(a) {
- return a.updated_at
- }), b.max_progress == b.screens && (b = null);
- else if (h.max_progress != h.screens) b = h;
- else if (f == c.length - 1) b = null;
- else {
- var i = c.slice(f).filter(function(a) {
- return a.max_progress < a.screens
- });
- b = i.length ? i[0] : null
- }
- if (!b)
- if (g) b = g;
- else {
- var j = c.filter(function(a) {
- return a.max_progress < a.screens
- });
- b = j.length ? j[0] : null
- }
- } else b = _.where(d, {
- course: a.id
- })[0];
- a.set({
- progress: e,
- active_lesson: b ? b.lesson_id : null,
- screens: c.reduce(function(a, b) {
- return a + b.screens
- }, 0)
- })
- }
- })
- }
- })
- }), define("global/collections/user_lesson_screens", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "lesson_screen_id",
- defaults: {
- lesson_id: 0,
- lesson_screen_id: 0,
- seconds: 0,
- errors: 0,
- typed: 0,
- stars: 0,
- created_at: 0
- }
- });
- return Backbone.Collection.extend({
- model: b,
- url: function() {
- return "/apiv1/student/lessons/" + this.options.lesson_id + "/screens"
- },
- initialize: function(a, b) {
- this.options = b
- }
- })
- }), define("shared/model_backends/localstorage", ["require"], function(a) {
- "use strict";
- var b = [],
- c = function(a) {
- this.table = a
- };
- return c.prototype.setModel = function(a) {
- b.push(a), this.model = a
- }, c.prototype.getData = function() {
- return "undefined" == typeof window.localStorage[rot47(this.table)] ? null : JSON.parse(rot47(window.localStorage[rot47(this.table)]))
- }, c.prototype.setData = function(a) {
- window.localStorage[rot47(this.table)] = rot47(JSON.stringify(a))
- }, c.prototype.change = function() {
- this.setData(this.model.toJSON())
- }, c.prototype.add = c.prototype.change, c.prototype.remove = c.prototype.change, c.prototype.reset = c.prototype.change, c.prototype.sort = c.prototype.change, c.clear = function() {
- b.forEach(function(a) {
- a.off("change")
- }), localStorage.clear()
- }, c
- }), define("index/index", ["require", "templates", "registry", "shared/scoring", "index/views/dashboard", "index/views/migration", "global/collections/courses", "global/collections/lessons", "global/collections/user_lesson_screens", "shared/model_backends/localstorage", "global/views/confirm"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = (a("shared/scoring"), a("index/views/dashboard")),
- e = a("index/views/migration"),
- f = a("global/collections/courses"),
- g = a("global/collections/lessons"),
- h = a("global/collections/user_lesson_screens"),
- i = a("shared/model_backends/localstorage"),
- j = a("global/views/confirm");
- return Backbone.View.extend({
- el: "#js-content",
- events: {
- "mouseenter .path": "showCourse",
- "mouseenter .lesson": "hoverLesson",
- "mouseleave .lesson": "hoverLesson",
- "mouseenter .path .button": "hoverButton",
- "mouseleave .path .button": "hoverButton",
- "click .restart": "restartLesson"
- },
- initialize: function(a) {
- this.options = a || {}, this.user = c.get("user"), this.courses = new f(FTWGLOBALS("courses")), this.lessons = new g(FTWGLOBALS("lessons")), this.userLessons = c.get("userLessons"), FTWGLOBALS("loggedIn") || (this.userLessons.forEach(function(a) {
- new i("userLessonScreens" + a.id).setData(null)
- }), this.userLessons.reset(), this.user.clearLocalProgress()), this.dashboardView = new d({}), this.updateModelsWithProgress(), void 0 === this.template && (this.template = b("index", "index")), this.render()
- },
- restartLesson: function(a) {
- var b = $(a.currentTarget),
- c = b.data("id"),
- d = this.userLessons.get(c),
- e = new j({
- title: "Restart Lesson: " + this.lessons.get(c).get("name"),
- text: "Are you sure you would like to restart the lesson?",
- doNotClose: !0
- });
- return e.show().once("close", function(a) {
- a ? d.restart().done(function() {
- d.set({
- progress: 0,
- max_progress: 0,
- stars: 0,
- typed: 0,
- seconds: 0,
- errors: 0
- });
- var a = new h(null, {
- lesson_id: d.id
- });
- a.setBackend(new i("userLessonScreens" + d.id), "backend"), a.reset(), location.href = "/student/lessons/" + d.id
- }.bind(this)).fail(function() {
- alert("There was an error resetting your lesson!")
- }) : e.close()
- }.bind(this)), !1
- },
- updateModelsWithProgress: function() {
- this.lessons.setProgress(this.userLessons), this.courses.setProgress(this.lessons)
- },
- createPie: function(a) {
- var b, c, d, e, f, g, h, i = this.lessons.where({
- course: a
- }),
- j = i.length,
- k = 360 * (1 / j),
- l = 0,
- m = -90;
- b = this.$("#course-pie-" + a)[0], b && _.sortBy(i, function(a) {
- return a.get("max_progress") == a.get("screens") ? 0 : 1
- }).forEach(function(a, i) {
- l = m, m = l + k, c = parseInt(Math.round(200 + 195 * Math.cos(Math.PI * l / 180))), e = parseInt(Math.round(200 + 195 * Math.sin(Math.PI * l / 180))), d = parseInt(Math.round(200 + 195 * Math.cos(Math.PI * m / 180))), f = parseInt(Math.round(200 + 195 * Math.sin(Math.PI * m / 180))), g = "M200,200 L" + c + "," + e + " A195,195 0 " + (m - l > 180 ? 1 : 0) + ",1 " + d + "," + f + " z", h = document.createElementNS("http://www.w3.org/2000/svg", "path"), h.setAttribute("d", g), h.setAttribute("class", a.get("max_progress") == a.get("screens") ? "complete" : ""), b.appendChild(h)
- })
- },
- render: function() {
- var a = this.userLessons.sortBy(function(a) {
- return -a.get("updated_at")
- })[0],
- b = a && this.lessons.get(a.id) ? this.lessons.get(a.id).get("course") : "beginner";
- if ("test" == b && (b = "beginner"), this.$el.append(this.template({
- loggedIn: FTWGLOBALS("loggedIn"),
- currentCourse: b,
- courses: this.courses.toJSON(),
- lessons: this.lessons.toJSON(),
- user: this.user.toJSON()
- })), 1 == this.user.get("migrated")) {
- new e({
- el: this.$("#migrationContent")
- })
- }
- this.dashboardView && this.$("#dashboardContent").html(this.dashboardView.el), this.courses.forEach(function(a) {
- this.createPie(a.id)
- }.bind(this))
- },
- showCourse: function(a) {
- var b = $(a.currentTarget);
- if (b.hasClass("active")) return !1;
- var c = this.$("#lessons-course-" + $(a.currentTarget).data("id")),
- d = this.$(".path.active"),
- e = this.$("#lessons-course-" + d.data("id"));
- d.removeClass("active"), e.fastHide(), b.addClass("active"), c.fastShow()
- },
- hoverLesson: function(a) {
- "mouseenter" == a.type ? $(a.currentTarget).velocity("stop", !0).velocity({
- scale: [1.02, 1]
- }, 80, "ease-in") : $(a.currentTarget).velocity("stop", !0).velocity("reverse", 50)
- },
- hoverButton: function(a) {
- "mouseenter" == a.type ? $(a.currentTarget).velocity("stop", !0).velocity({
- scale: [1.1, 1]
- }, 150) : $(a.currentTarget).velocity("stop", !0).velocity("reverse")
- }
- })
- }), define("oauth/index", ["require", "registry"], function(a) {
- "use strict";
- var b = a("registry");
- return Backbone.View.extend({
- initialize: function() {
- var a = {
- tz: jstz.determine().name(),
- screenWidth: screen.width
- };
- $.post("/apiv1/student/auth/oauth-login", a).done(function(a) {
- b.get("user").loginWithData(a.data), window.location.href = "/student"
- }).fail(function(a) {
- a.responseJSON && a.responseJSON.data.message ? this.showError("Uh oh, an error occurred", a.responseJSON.data.message) : this.showError("Uh oh, an error occurred", a.responseText)
- }.bind(this))
- },
- showError: function(a, b) {
- $("#title").velocity({
- opacity: [0, 1]
- }, function() {
- $(this).html(a).velocity({
- opacity: [1, 0]
- }), $("#message").html(b), $(".loader").fastHide()
- })
- }
- })
- }), define("shared/analytics", ["require"], function(a) {
- "use strict";
- return {
- trackEvent: function(a, b, c, d) {
- ga("send", "event", a, b, c, d)
- },
- trackPage: function(a) {
- ga("send", "pageview", a)
- },
- customDimension: function(a, b) {
- ga("set", "dimension" + a, b)
- },
- customMetric: function(a, b) {
- ga("set", "metric" + a, b)
- },
- trackSale: function(a, b) {
- ga("require", "ecommerce"), ga("ecommerce:addTransaction", {
- id: a.transaction_id,
- affiliation: a.store,
- revenue: a.total,
- shipping: 0,
- tax: 0
- }), b && b.forEach(function(b) {
- ga("ecommerce:addItem", {
- id: a.transaction_id,
- name: b.product,
- sku: b.sku,
- category: b.category,
- price: b.price,
- quantity: b.quantity || 1
- })
- }), ga("ecommerce:send")
- }
- }
- }), define("shared/form_validation", ["require"], function(a) {
- "use strict";
- var b = function(a, b, c, d, e, f, g) {
- 1 == arguments.length ? (_.extend(this, arguments[0]), this.bindTo && this.successCallback && (this.successCallback = this.successCallback.bind(this.bindTo))) : (this.form = a, this.button = b, this.model = c, e && (d = d.bind(e)), this.successCallback = d || $.noop, this.remainDisabled = f), g && (c.fileUpload = a), this.setup()
- };
- return b.prototype.setup = function() {
- this.form.validate({
- submitHandler: _.bind(this.submitHandler, this),
- rules: this.model.validationRules || {},
- messages: this.model.validationMessages || {},
- invalidHandler: function(a, b) {
- var c = b.errorList.map(function(a) {
- return a.element
- });
- $.Velocity(c, "ftw.miniShake")
- }.bind(this)
- });
- var a = this;
- this.button.click(function(b) {
- b.preventDefault(), a.form.submit()
- })
- }, b.prototype.submitHandler = function(a) {
- if (this.options = a || {
- delay: 300
- }, !this.submitted) {
- this.disableButton();
- var b = {},
- c = this.model.toJSON();
- _.each(this.form.serializeArray(), function(a) {
- a.value != c[a.name] && (b[a.name] = a.value)
- }), setTimeout(function() {
- this.save(b, {
- patch: !0,
- silent: !1,
- wait: !0
- })
- }.bind(this), this.options.delay || 0)
- }
- }, b.prototype.saveSuccess = function(a) {
- this.successCallback(a.data, this.model, this.form), this.remainDisabled || this.enableButton()
- }, b.prototype.saveError = function(a) {
- if (a = a || {}, $._lastXhr = {
- response: JSON.stringify(a)
- }, "object" == typeof a && (a = a.responseJSON || a.responseText || a.data), this.failureCallback && this.failureCallback(a), a) {
- var b = this.model.validationMessages || {};
- if ("object" == typeof a) {
- _.each(a, function(c, d) {
- b[d] && b[d][c] && (a[d] = b[d][c])
- }.bind(this)), this.form.validate().showErrors(a), $.Velocity($(".error", this.form), "ftw.miniShake");
- var c = Object.keys(a)[0];
- this.form.find("[name=" + c + "]").focus()
- } else alert("Response from the server was invalid. Response: " + JSON.stringify(a))
- } else alert("There was an error communicating with the server: No response was returned. Please check your internet connection and try again.");
- this.enableButton()
- }, b.prototype.disableButton = function() {
- this.button.addClass("pending"), this.submitted = !0
- }, b.prototype.enableButton = function() {
- this.button.removeClass("pending"), this.submitted = !1
- }, b.prototype.save = function(a, b) {
- this.model.save(a, b).done(function(a) {
- a.success ? this.saveSuccess(a) : this.saveError(a)
- }.bind(this)).fail(function(a) {
- var b = a.responseJSON || a.responseText;
- this.saveError(b)
- }.bind(this))
- }, b
- }), define("shared/form_model", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- initialize: function() {
- $.validator.addMethod("alphanum", function(a) {
- return /^[a-zA-Z0-9]+$/.test(a)
- }, "Can only contain letters and numbers")
- },
- validationRules: {},
- validationMessages: {},
- sync: function(a) {
- if ("read" == a || "delete" == a || !this.fileUpload) return Backbone.Model.prototype.sync.apply(this, arguments);
- var b = this.id ? this.urlRoot + "/" + this.id : this.urlRoot,
- c = this.id ? "patch" : "post";
- return $.ajax({
- url: b,
- method: "POST",
- data: new FormData($(this.fileUpload)[0]),
- dataType: "json",
- processData: !1,
- contentType: !1,
- headers: {
- "X-HTTP-METHOD-OVERRIDE": c
- }
- }).done(function(a) {
- this.set(a.data)
- }.bind(this))
- }
- });
- return b
- }), define("global/models/signup", ["require", "shared/form_model"], function(a) {
- "use strict";
- var b = a("shared/form_model");
- return b.extend({
- initialize: function() {},
- url: "/apiv1/student/auth/signup",
- defaults: {
- username: "",
- password: "",
- password2: "",
- email: "",
- tz: jstz.determine().name()
- },
- validationRules: {
- username: {
- minlength: 4,
- maxlength: 30,
- username: !0,
- required: !0,
- remote: {
- url: "/apiv1/student/auth/username",
- type: "post"
- }
- },
- email: {
- require: !1,
- email: !0,
- emailExtended: !0,
- remote: {
- url: "/apiv1/student/auth/email",
- type: "post"
- }
- },
- password: {
- required: !0,
- minlength: 4,
- notMatchUsername: !0
- },
- password2: {
- required: !0,
- minlength: 4,
- equalTo: "#reg-form input[name=password]"
- }
- },
- validationMessages: {
- username: {
- remote: "This username already exists. Please try a different username.",
- EXISTS: "This username already exists. Please try a different username.",
- minlength: "Username must be at between 4 and 30 characters in length",
- maxlength: "Username must be at between 4 and 30 characters in length",
- INVALID_LENGTH: "Username must be at between 4 and 30 characters in length",
- INVALID_CHARS: "Can only contain letters, numbers, and underscores",
- INVALID_IP: "Your teacher has restricted access to only specific locations. Your IP address does not match the list of allowed IPs.",
- NO_MOBILE_ACCESS: "Your teacher has restricted access form small/mobile devices. Please log in from a computer."
- },
- email: {
- email: "Invalid email address. Keep in mind, your email is optional.",
- remote: 'An account with this email already exists. If you lost your password, you should use the "I forgot my login" feature to recover it.',
- EXISTS: 'An account with this email already exists. If you lost your password, you should use the "I forgot my login" feature to recover it.',
- INVALID: "Invalid email address, please double check. Keep in mind, your email is optional."
- },
- password: {
- notMatchUsername: "Password can not be the same as your username",
- NO_MATCH_USERNAME: "Password can not be the same as your username",
- minlength: "Password must be at least 4 characters long",
- INVALID_LENGTH: "Password must be at least 4 characters long"
- },
- password2: {
- required: "Please confirm your password",
- equalTo: "Passwords do not match",
- NO_MATCH: "Passwords do not match"
- },
- section_id: {
- INVALID: "Invalid Section ID or code"
- }
- }
- })
- }), define("global/views/signup_form", ["require", "templates", "shared/analytics", "registry", "shared/form_validation", "global/models/signup"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/analytics"),
- d = a("registry"),
- e = a("shared/form_validation"),
- f = a("global/models/signup");
- return Backbone.View.extend({
- events: {
- "keypress #reg-form input": "submitOnEnter"
- },
- initialize: function(a) {
- a.template ? this.template = a.template : this.template = b("global", "signup_form"), a.classes && (this.classes = a.classes), a.joinCode && (this.joinCode = a.joinCode), this.model = new f, this.render(), this.form = this.$("#reg-form");
- var c = this.$("#reg-form .submit");
- new e({
- form: this.form,
- button: c,
- model: this.model,
- successCallback: this.successCallback.bind(this),
- remainDisabled: !0
- })
- },
- render: function() {
- this.$el.html(this.template({
- classes: this.classes,
- joinCode: this.joinCode
- }))
- },
- successCallback: function(a) {
- c.trackPage("/student/signup/success"), d.get("user").loginWithData(a), window.location.href = "/student"
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("global/views/modal", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- contentView: null,
- className: "popup-wrap",
- events: {
- "click .close": "hide",
- "click .ok": "ok"
- },
- initialize: function(a) {
- if (this.options = a || {}, this.template = b("global", "modal"), !a || !_.isObject(a.contentView)) throw new Error("Cannot create a modal without a contentView");
- this.contentView = a.contentView, this.listenTo(this.contentView, "complete", function(a) {
- this.trigger("complete", a), this.hide()
- }), this.render()
- },
- render: function() {
- return this.$el.html(this.template({
- title: this.options.title,
- okButton: this.options.okButton,
- cancelButton: this.options.cancelButton,
- closeButton: void 0 === this.options.closeButton ? !0 : this.options.closeButton,
- width: this.options.width || null
- })).fastHide(), this.contentView.button = this.$(".ok"), this.$(".popup-content").html(this.contentView.render().el), this
- },
- ok: function() {
- return this.contentView.ok(), !1
- },
- show: function() {
- var a;
- if ("fixed" == this.options.position) $("body").append(this.el), a = this.$el;
- else {
- var b = $("<div>"),
- c = this.$el,
- d = this.$(".popup");
- d.addClass("popup-absolute"), $("body").append(b), this.setElement(b), b.append(c), b.append(d), d.css({
- top: $(window).scrollTop() + 50,
- left: $(window).width() / 2 - d.outerWidth() / 2
- }), a = c
- }
- a.velocity("fadeIn", function() {
- this.$el.find("input[type=text]:eq(0)").focus()
- }.bind(this))
- },
- hide: function() {
- return this.$el.velocity("fadeOut", this.remove.bind(this)), !1
- }
- })
- }), define("global/views/notice", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- events: {
- "click span": "close",
- mouseover: "hover"
- },
- initialize: function(a) {
- this.options = a || {}, this.template = b("global", "notice")
- },
- render: function() {
- var a = $("#notices");
- return a.length || (a = $('<div id="notices"></div>'), $("#js-content").prepend(a)), this.$el.html(this.template({
- error: this.options.error,
- text: this.options.text
- })).fastHide().appendTo(a), this
- },
- close: function() {
- return clearTimeout(this.timeout), this.$el.velocity("fadeOut", this.remove.bind(this)), !1
- },
- show: function() {
- this.render(), this.$el.velocity("fadeIn"), this.timeout = setTimeout(this.close.bind(this), 5e3)
- },
- hover: function() {
- clearTimeout(this.timeout)
- }
- })
- }), define("global/views/alert", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- events: {
- "click .ok": "close"
- },
- initialize: function(a) {
- this.options = a || {}, this.options.position = "fixed", this.template = b("global", "alert")
- },
- render: function() {
- return this.setElement(this.template({
- title: this.options.title,
- text: this.options.text,
- ok: this.options.ok
- })), this.$el.fastHide(), $("body").append(this.el), this
- },
- close: function() {
- return this.trigger("close"), this.$el.velocity("fadeOut", this.remove.bind(this)), !1
- },
- show: function() {
- return this.render(), this.$el.velocity("fadeIn", function() {
- this.$el.find("input:eq(0)").focus()
- }.bind(this)), this
- }
- })
- }), define("global/models/login", ["require", "shared/form_model"], function(a) {
- "use strict";
- var b = a("shared/form_model");
- return b.extend({
- url: "/apiv1/student/auth/login",
- defaults: {
- username: null,
- password: null,
- tz: jstz.determine().name(),
- screenWidth: screen.width,
- adb: !1
- },
- validationRules: {
- username: {
- required: !0
- },
- password: {
- required: !0
- }
- },
- validationMessages: {
- username: {
- INVALID_USERNAME: "This username does not exist",
- CLASS_MIGRATION: "This account has not been migrated",
- INVALID_IP: "Your teacher has restricted access to only specific locations. Your IP address does not match the list of allowed IPs.",
- NO_MOBILE_ACCESS: "Your teacher has restricted access form small/mobile devices. Please log in from a computer."
- },
- password: {
- INCORRECT_PASSWORD: "Your password is incorrect for this username, please try again"
- }
- }
- })
- }), define("global/views/forgot_password", ["require", "templates", "shared/form_model", "shared/form_validation"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/form_model"),
- d = a("shared/form_validation"),
- e = c.extend({
- url: "/apiv1/student/auth/password-reset",
- defaults: {
- email: ""
- },
- validationRules: {
- email: {
- email: !0,
- emailExtended: !0,
- required: !0
- }
- }
- });
- return Backbone.View.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- baseModel: null,
- initialize: function(a) {
- this.options = a, this.template = b("global", "forgot_password"), this.templateNoMigrate = b("global", "forgot_password_no_migration"), this.model = new e
- },
- render: function() {
- return this.$el.html(this.template({})), this.form = this.$("form"), new d({
- form: this.form,
- button: this.button,
- model: this.model,
- successCallback: this.successCallback.bind(this),
- failureCallback: this.failureCallback.bind(this),
- bindTo: this,
- remainDisabled: !0
- }), this
- },
- ok: function() {
- this.notMigrated ? location.href = "https://classic.typing.com/tutor/" : this.form.submit()
- },
- successCallback: function(a) {
- this.trigger("complete", a)
- },
- failureCallback: function(a) {
- a && "NOT_MIGRATED" == a.email && (this.notMigrated = !0, this.$el.html(this.templateNoMigrate()))
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("global/views/login_form", ["require", "templates", "registry", "shared/form_validation", "global/views/modal", "global/views/notice", "global/views/alert", "global/models/login", "global/views/forgot_password"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/form_validation"),
- e = a("global/views/modal"),
- f = a("global/views/notice"),
- g = a("global/views/alert"),
- h = a("global/models/login"),
- i = a("global/views/forgot_password");
- return Backbone.View.extend({
- events: {
- "keypress #login-form input": "submitOnEnter",
- "click .forgot-password-callout": "forgotPassword"
- },
- initialize: function() {
- this.template = b("global", "login_form"), this.model = new h, this.render(), this.form = this.$("#login-form");
- var a = this.$("#login-form .submit");
- new d({
- form: this.form,
- button: a,
- model: this.model,
- failureCallback: this.failureCallback.bind(this),
- successCallback: this.successCallback.bind(this),
- remainDisabled: !0
- })
- },
- render: function() {
- this.$el.html(this.template({
- username: location.hash.replace("#", "") || ""
- }))
- },
- successCallback: function(a) {
- c.get("user").loginWithData(a), window.location.href = "/student"
- },
- failureCallback: function(a) {
- a && "CLASS_MIGRATION" == a.username && new g({
- title: "Oh no!",
- text: 'Your teacher has not yet migrated to the new version of Typing.com.</p><p>To continue your lessons, please go to <a href="http://classic.typing.com/tutor/">http://classic.typing.com/tutor</a> and log in.'
- }).show()
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- },
- forgotPassword: function() {
- var a = new e({
- title: "I forgot my login!",
- okButton: "Continue",
- cancelButton: "Cancel",
- contentView: new i,
- width: 500
- });
- return a.on("complete", function(a) {
- a && a.bounce ? new f({
- error: !0,
- text: "An email has been sent with instructions for changing your password.<br /><br />However, the last email we sent you was undeliverable. Your mail service returned the error: " + a.bounce + "<br /><br />Because of this, it is possible you will not receive this email."
- }).show() : new f({
- error: !1,
- text: "An email has been sent with instructions for changing your password."
- }).show()
- }.bind(this)), a.show(), !1
- }
- })
- }), define("global/views/login", ["require", "templates", "global/views/signup_form", "global/views/login_form"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("global/views/signup_form"),
- d = a("global/views/login_form");
- return Backbone.View.extend({
- events: {
- "click #cleverLogin": "cleverLogin",
- "click #googleLogin": "googleLogin"
- },
- initialize: function(a) {
- this.options = a || {}, void 0 === this.template && (this.template = b("global", "login")), this.render(), new c({
- el: this.$("#signupContent")
- }), new d({
- el: this.$("#loginContent")
- }), this.focus()
- },
- render: function() {
- return this.$el.html(this.template({
- hideTeacherCallout: this.options.hideTeacherCallout
- })), this
- },
- focus: function() {
- "/student/signup" == location.pathname ? $("#reg-form input[name=username]").focus() : $("#login-form input[name=username]").focus()
- },
- getState: function() {
- var a = _.random(1e4, 99999);
- return $.cookie("state", a, {
- path: "/"
- }), a
- },
- cleverLogin: function() {
- return location.href = "https://clever.com/oauth/authorize?client_id=" + FTWGLOBALS("cleverClientId") + "&redirect_uri=" + encodeURIComponent(FTWGLOBALS("cleverRedirectUri")) + "&response_type=code&state=" + this.getState(), !1
- },
- googleLogin: function() {
- return location.href = "https://accounts.google.com/o/oauth2/auth?client_id=" + FTWGLOBALS("googleClientId") + "&redirect_uri=" + encodeURIComponent(FTWGLOBALS("googleRedirectUri")) + "&response_type=code&fetch_basic_profile=true&scope=openid%20email%20profile&state=" + this.getState(), !1
- }
- })
- }), define("index/login", ["require", "global/views/login"], function(a) {
- "use strict";
- var b = a("global/views/login");
- return Backbone.View.extend({
- el: "#js-content",
- initialize: function() {
- new b({
- el: this.el
- })
- }
- })
- }), define("join/views/classes_modal", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- initialize: function(a) {
- this.options = a, this.template = b("join", "classes_modal")
- },
- render: function() {
- return this.$el.html(this.template({
- classes: this.options.classes
- })), this
- },
- ok: function() {
- this.successCallback()
- },
- successCallback: function() {
- this.trigger("complete", this.$("select[name=section_id]").val())
- }
- })
- }), define("join/index", ["require", "templates", "global/views/signup_form", "global/views/modal", "join/views/classes_modal"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("global/views/signup_form"),
- d = a("global/views/modal"),
- e = a("join/views/classes_modal");
- return Backbone.View.extend({
- el: "#js-content",
- events: {
- "click #cleverLogin": "cleverLogin",
- "click #googleLogin": "googleLogin"
- },
- initialize: function(a) {
- this.options = a, this.template = b("join", "index"), this.formTemplate = b("join", "form"), this.failedTemplate = b("join", "failed"), $.post("/apiv1/student/auth/join-validate", {
- join_code: a.code
- }).done(function(a) {
- this.classes = a.data, this.render()
- }.bind(this)).fail(this.renderFailed.bind(this))
- },
- render: function() {
- this.$el.html(this.template()), new c({
- el: this.$("#signupContent"),
- template: this.formTemplate,
- classes: this.classes,
- joinCode: this.options.code
- })
- },
- renderFailed: function() {
- this.$el.html(this.failedTemplate())
- },
- getState: function() {
- var a = _.random(1e4, 99999);
- return $.cookie("state", a, {
- path: "/"
- }), a
- },
- cleverLogin: function() {
- return location.href = "https://clever.com/oauth/authorize?client_id=" + FTWGLOBALS("cleverClientId") + "&redirect_uri=" + encodeURIComponent(FTWGLOBALS("cleverRedirectUri")) + "&response_type=code&state=" + this.getState(), !1
- },
- googleLogin: function() {
- if ($.cookie("_joinCode", this.options.code, {
- path: "/"
- }), 1 == this.classes.length) return $.cookie("_joinClassId", this.classes[0].section_id, {
- path: "/"
- }), this.completeGoogleLogin();
- var a = new d({
- width: 450,
- title: "Select Your Class",
- okButton: "Continue",
- cancelButton: "Cancel",
- contentView: new e({
- classes: this.classes
- })
- });
- return a.on("complete", function(b) {
- $.cookie("_joinClassId", b, {
- path: "/"
- }), a.hide(), this.completeGoogleLogin()
- }.bind(this)), a.show(), !1
- },
- completeGoogleLogin: function() {
- return location.href = "https://accounts.google.com/o/oauth2/auth?client_id=" + FTWGLOBALS("googleClientId") + "&redirect_uri=" + encodeURIComponent(FTWGLOBALS("googleRedirectUri")) + "&response_type=code&scope=openid&state=" + this.getState(), !1
- }
- })
- }), define("password/index", ["require", "templates", "shared/form_model", "shared/form_validation", "global/views/notice"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/form_model"),
- d = a("shared/form_validation"),
- e = (a("global/views/notice"), c.extend({
- initialize: function() {},
- url: "/apiv1/student/auth/password-update",
- defaults: {
- hash: "",
- user_id: "",
- username: "",
- password: "",
- password2: ""
- },
- validationRules: {
- password: {
- required: !0,
- minlength: 4
- },
- password2: {
- required: !0,
- minlength: 4,
- equalTo: "#password-form input[name=password]"
- }
- },
- validationMessages: {
- password: {
- required: "Please enter a password"
- },
- password2: {
- required: "Please confirm your password",
- equalTo: "Passwords do not match"
- }
- }
- })),
- f = Backbone.View.extend({
- events: {
- "keypress #password-form input": "submitOnEnter"
- },
- initialize: function(a) {
- this.model = new e(a.data), this.form = this.$("#password-form");
- var b = this.$("#password-form .submit");
- new d(this.form, b, this.model, this.successCallback, this, !0)
- },
- successCallback: function(a) {
- this.trigger("complete")
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- });
- return Backbone.View.extend({
- el: "#js-content",
- initialize: function(a) {
- this.options = a || {}, this.template = b("password", "index"), this.failedTemplate = b("password", "failed"), this.completeTemplate = b("password", "complete");
- var c = location.hash.replace("#", "").split("!"),
- d = c[0],
- e = c[1];
- $.post("/apiv1/student/auth/password-validate", {
- hash: d,
- user_id: e
- }).done(function(a) {
- this.data = {
- username: a.data.username,
- hash: d,
- user_id: e
- }, this.render()
- }.bind(this)).fail(this.renderFailed.bind(this))
- },
- render: function() {
- this.$el.html(this.template(this.data));
- var a = new f({
- el: this.el,
- data: this.data
- });
- return a.on("complete", this.renderComplete.bind(this)), this.$("#password-form input[name=password]").focus(), this
- },
- renderFailed: function() {
- this.$el.html(this.failedTemplate())
- },
- renderComplete: function() {
- this.$el.velocity("slideUp", function() {
- this.$el.html(this.completeTemplate()).velocity("slideDown")
- }.bind(this))
- }
- })
- }), define("scoreboard/collections/scoreboard", ["require", "shared/scoring"], function(a) {
- "use strict";
- var b = a("shared/scoring");
- return Backbone.Collection.extend({
- model: Backbone.Model.extend({
- idAttribute: "user_id"
- }),
- url: "/apiv1/student/scoreboard",
- parse: function(a) {
- var c = a.data;
- return _.isArray(c) && c.forEach(function(a) {
- a.speed = b.speed(a.typed, a.seconds, a.errors), a.accuracy = b.accuracy(a.typed, a.errors)
- }), Backbone.Collection.prototype.parse.call(this, a)
- },
- comparatorKey: "speed",
- comparator: function(a) {
- return -a.get(this.comparatorKey)
- }
- })
- }), define("scoreboard/index", ["require", "templates", "registry", "scoreboard/collections/scoreboard"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("scoreboard/collections/scoreboard");
- return Backbone.View.extend({
- el: "#js-content",
- events: {
- "click .type": "changeType"
- },
- initialize: function() {
- this.template = b("scoreboard", "index"), this.collection = new d, this.collection.fetch({
- reset: !0
- }), this.listenTo(this.collection, "reset", this.render), this.user = c.get("user")
- },
- render: function() {
- this.$el.html(this.template({
- type: this.collection.comparatorKey,
- data: this.collection.toJSON()
- }))
- },
- changeType: function(a) {
- var b = $(a.currentTarget);
- return b.hasClass("active") ? !1 : (this.collection.comparatorKey = b.data("id"), this.collection.sort(), this.render(), !1)
- }
- })
- }), define("verify/models/test", ["require", "shared/form_model"], function(a) {
- "use strict";
- var b = a("shared/form_model");
- return b.extend({
- idAttribute: "user_test_id",
- url: function() {
- return "/apiv1/student/tests/" + this.id + "/verify?user_id=" + this.get("user_id")
- }
- })
- }), define("verify/index", ["require", "templates", "registry", "verify/models/test"], function(a) {
- "use strict";
- var b = a("templates"),
- c = (a("registry"), a("verify/models/test"));
- return Backbone.View.extend({
- el: "#js-content",
- initialize: function(a) {
- this.template = b("verify", "index"), this.model = new c(a), this.model.fetch().done(this.render.bind(this))
- },
- render: function(a) {
- this.$el.html(this.template({
- data: this.model.toJSON()
- }))
- }
- })
- }), define("shared/radar_tracker", ["require"], function(a) {
- "use strict";
- return {
- track: function(a, b) {
- if (a.get("teacher_id")) {
- var c = {
- t: a.get("teacher_id"),
- u: a.id
- };
- b && (c.l = {
- t: b.typed,
- s: b.seconds,
- e: b.errors,
- p: b.progress
- }), $.get("/apiv1/student/radar/" + window.btoa(JSON.stringify(c)))
- }
- }
- }
- }), define("lessons/models/screen", ["require"], function(a) {
- "use strict";
- return Backbone.Model.extend({
- idAttribute: "lesson_screen_id",
- defaults: {
- content: "",
- formatted_content: "",
- lastKey: "",
- line: 0,
- typed: 0,
- errors: 0,
- seconds: 0,
- lastKeyCode: 0,
- lastKeyError: !1
- },
- formatContent: function(a, b) {
- var c = this.get("content"),
- d = this.get("title"),
- e = this.get("intro");
- c = c.replace(/\r/g, "").replace(/\s+\n/g, "\n").trim(), "speed" == this.get("screen_type") && (c = c + "\n" + c + "\n" + c + "\n" + c + "\n"), c = this.periodSpacing(c, b.get("spaces"));
- var f = c;
- c = c.replace(/âŽ\n/g, "âŽ"), this.set({
- title: d,
- intro: e,
- content: c,
- formatted_content: f
- })
- },
- getContentByLine: function() {
- return _.compact(this.get("formatted_content").split(/\n/)).map(function(a, b, c) {
- return a = a.split(""), b < c.length && "âŽ" != a[a.length - 1] && a.push("\n"), a
- })
- },
- charAt: function(a) {
- var b = this.get("content") || "";
- return b[a]
- },
- handleInput: function(a) {
- var b = this.get("typed"),
- c = this.get("screen_type"),
- d = this.get("errors"),
- e = this.get("line"),
- f = b,
- g = a.key,
- h = this.charAt(f),
- i = "\n" == h || "âŽ" == h,
- j = !1;
- if ("\n" == h && " " == g && "speed" != c && (h = " "), "âŽ" == h && "\n" == g && (h = "\n"), !(f >= this.get("content").length)) {
- if ("BACKSPACE" == g) {
- if ("falling" == c) return;
- if ("speed" == c && ("\n" == this.charAt(f - 1) || "âŽ" == this.charAt(f - 1))) return;
- "\n" != this.charAt(f - 1) && "âŽ" != this.charAt(f - 1) || e--, f > 0 && (f -= 1)
- } else {
- if (a.special) return;
- h == g ? (f += 1, i && e++) : h != g && (j = !0, this.get("lastKeyError") || (f += "falling" == this.get("screen_type") ? 0 : 1, d += 1, i && e++))
- }
- this.set({
- typed: f,
- nextKey: this.charAt(f),
- letterTyped: g,
- lastKeyError: j,
- errors: d,
- correctLetter: h,
- line: e
- }), f >= this.get("content").length && this.trigger("complete")
- }
- },
- periodSpacing: function(a, b) {
- if (b = b || 1, 1 > b || b > 2) throw new Error("Only 1 or 2 spaces after a period");
- var c = 1 == b ? " " : " ";
- return a.replace(/(\.|\?|!) +/g, "$1" + c).replace(/(mrs\.|mr\.|sr\.|st\.|ms\.|\s\w\.|inc\.|lt\.|jr\.|dr\.|\sgt\.|ft\.|st\.|ave\.|rd\.|in\.|hrs\.|e\.g\.|ot\.|lol\.|ed\.|fl\.|off\.|ord\.|rt\.|rds\.|asstd\.|ea\.|ltd\.|ins\.|reg\.|c\.o\.d\.) /gi, "$1 ")
- },
- metMinimums: function(a, b) {
- return a > 0 && this.speed() < a ? !1 : !(b > 0 && this.accuracy() < b)
- },
- quickComplete: function() {
- this.set({
- typed: 1
- }), this.trigger("complete")
- },
- setPeriodSpacing: function(a) {
- this.set({
- content: this.periodSpacing(this.get("content"), a),
- period_spacing: a
- })
- }
- })
- }), define("lessons/collections/screens", ["require", "lessons/models/screen"], function(a) {
- "use strict";
- var b = a("lessons/models/screen");
- return Backbone.Collection.extend({
- model: b,
- comparator: "display_order"
- })
- }), define("lessons/collections/problemkey_screens", ["require", "lessons/models/screen"], function(a) {
- "use strict";
- var b = a("lessons/models/screen");
- return Backbone.Collection.extend({
- constructor: function(a, b) {
- for (var c = b.letters.map(function(a) {
- return a.id.toUpperCase()
- }), d = _.shuffle(_.flatten(_.map(c, function(a) {
- return b.words[a]
- }))), e = [], f = 0, g = 0;;) {
- for (e[f] || (e[f] = "");;) {
- if ((e[f] + " " + d[g]).length >= 50) {
- e[f] = e[f].trim(), f++;
- break
- }
- e[f] += " " + d[g], g++
- }
- if (f > 100) break
- }
- return a[0].intro = a[0].intro.format(c.join(", ")), a[0].title += c.join(", "), a[0].content = e.join("\n"), Backbone.Collection.call(this, a, b)
- },
- model: b,
- comparator: "display_order"
- })
- }), define("lessons/views/nav", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- initialize: function(a) {
- void 0 === this.template && (this.template = b("lessons", "nav")), this.lesson = a.lesson, this.screen = a.screen
- },
- render: function() {
- return this.$el.html(this.template({
- lesson: this.lesson.toJSON(),
- screen: this.screen ? this.screen.toJSON() : {}
- })), this
- }
- })
- }), define("global/views/keyboard", ["require", "templates", "registry"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry");
- return Backbone.View.extend({
- highlighted: [],
- lastFingers: [],
- events: {
- "click .hide-keyboard": "toggle"
- },
- initialize: function(a) {
- void 0 === this.template && (this.template = b("global", "keyboard")), this.user = c.get("user"), this.setKeyboard(a)
- },
- setKeyboard: function(a) {
- return this.options = a || {
- type: "small",
- noHands: !1,
- noControls: !1
- }, a.model && (this.model = a.model), this
- },
- toggleCapsLockWarning: function(a) {
- this.capsLockWarning && (a && this.capsLockWarning.is(":hidden") ? this.capsLockWarning.velocity("fadeIn") : !a && this.capsLockWarning.is(":visible") && this.capsLockWarning.velocity("fadeOut"))
- },
- render: function() {
- return this.$el.html(this.template({
- format: this.model.get("type"),
- structure: this.model.get("structure"),
- type: this.options.type,
- noHands: this.options.noHands,
- noControls: this.options.noControls,
- hideKeyboard: this.user.get("hideKeyboard")
- })), "large" == this.options.type && (this.hands = this.$("#handsImg")[0]), this.capsLockWarning = this.$(".capslock-warning"), this
- },
- highlightKey: function(a) {
- a = a || "", "\n" == a && (a = "âŽ");
- var b, c, d = "âŽ" == a ? 13 : a.toLowerCase().charCodeAt(0),
- e = this.$(".key-" + d),
- f = a != a.toLowerCase() || e.data("shifted") == d,
- g = e.data("finger");
- f && (5 >= g ? (b = this.rightCaps || this.$(".key-16.right"), this.rightCaps = b) : (b = this.leftCaps || this.$(".key-16.left"), this.leftCaps = b)), this.highlighted.forEach(function(a) {
- a[0].classList.remove("active")
- }), this.highlighted = [], "small" == this.options.type ? (this.lastFingers.forEach(function(a) {
- this.el.classList.remove("finger-" + a)
- }.bind(this)), this.lastFingers = []) : (this.lastFingers.forEach(function(a) {
- a.setAttribute("class", a.className.baseVal.replace("is-active", ""))
- }), this.lastFingers = []), a && (g && ("small" == this.options.type ? (this.$el[0].classList.add("finger-" + g), this.lastFingers.push(g), f && (5 >= g ? (this.$el[0].classList.add("finger-10"), this.lastFingers.push(10)) : (this.$el[0].classList.add("finger-1"), this.lastFingers.push(1)))) : (c = this.hands.querySelector("path.finger-" + g), this.lastFingers.push(c), c.setAttribute("class", c.className.baseVal + " is-active"), f && (c = 5 >= g ? this.hands.querySelector("path.finger-10") : this.hands.querySelector("path.finger-1"), this.lastFingers.push(c), c.setAttribute("class", c.className.baseVal + " is-active")))), f && (this.highlighted.push(b), b[0].classList.add("active")), e.length && (this.highlighted.push(e), e[0].classList.add("active")))
- },
- toggle: function() {
- this.$(".keyboard-container").toggleClass("hidden"), this.user.set({
- hideKeyboard: this.$(".keyboard-container").hasClass("hidden")
- })
- }
- })
- }), define("lessons/views/intro", ["require", "templates", "global/views/keyboard"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("global/views/keyboard");
- return Backbone.View.extend({
- events: {
- "click .begin-button": "begin"
- },
- animatedKeyId: 0,
- animationSpeed: 1e3,
- initialize: function(a) {
- this.options = a, void 0 === this.template && (this.template = b("lessons", this.options.test ? "test_intro" : "intro")), a.contents.match("virtual-keyboard") && (this.keyboard = new c({
- type: 29 == a.keyboard.id ? "small" : "large",
- noControls: !0,
- model: a.keyboard
- })), this.input = a.input
- },
- render: function() {
- if (this.listenTo(this.input, "keypress", this.handleKeypress), this.$el.html(this.template({
- contents: this.options.contents
- })), this.keyboard) {
- var a = this.$(".virtual-keyboard");
- 29 == this.options.keyboard && (this.keyboard.type = "small"), this.animationSpeed = a.data("keys-speed") || 1e3, this.keysToAnimate = String(a.data("keys")).split(" ").map(function(a) {
- return "space" == a ? " " : "quote" == a ? '"' : a
- }), a.html(this.keyboard.render().el), this.animateKeys()
- }
- return this
- },
- begin: function() {
- return this.trigger("begin"), !1
- },
- handleKeypress: function(a) {
- "\n" == a.key && this.begin()
- },
- animateKeys: function() {
- this.animatedKeyId >= this.keysToAnimate.length && (this.animatedKeyId = 0), this.keyboard.highlightKey(this.keysToAnimate[this.animatedKeyId]), this.animatedKeyId++, this.animationTimeout = setTimeout(this.animateKeys.bind(this), this.animationSpeed)
- },
- hide: function() {
- this.stopListening(this.input), clearTimeout(this.animationTimeout), this.$("#handsImg").remove(), this.$el.fastHide()
- }
- })
- }), define("lessons/views/congrats", ["require", "templates", "shared/scoring", "registry", "global/collections/badges", "global/collections/courses", "global/collections/lessons"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/scoring"),
- d = a("registry"),
- e = a("global/collections/badges"),
- f = a("global/collections/courses"),
- g = a("global/collections/lessons");
- return Backbone.View.extend({
- initialize: function(a) {
- void 0 === this.template && (this.template = b("lessons", "congrats")), this.userLessons = d.get("userLessons"), this.lessons = new g(FTWGLOBALS("lessons")), this.courses = new f(FTWGLOBALS("courses")), this.badges = new e, this.screen = a.screen, this.userLesson = a.userLesson, this.userLessonScreens = a.userLessonScreens, this.lesson = a.lesson
- },
- render: function() {
- this.lessons.setProgress(this.userLessons), this.courses.setProgress(this.lessons);
- var a = this.courses.get(this.lesson.get("course")).get("active_lesson"),
- b = this.lessons.get(a),
- d = [];
- return this.userLessonScreens.forEach(function(a) {
- for (var b = a.get("stars"), c = 0; b > c; c++) d.push(1);
- for (c = 0; 3 - b > c; c++) d.push(0)
- }), this.$el.html(this.template({
- lesson: this.lesson.toJSON(),
- starList: d,
- stars: this.userLesson.get("stars"),
- totalStars: 3 * this.lesson.get("screens"),
- accuracy: c.accuracy(this.userLesson.get("typed"), this.userLesson.get("errors")),
- speed: c.speed(this.userLesson.get("typed"), this.userLesson.get("seconds"), this.userLesson.get("errors")),
- time: (this.userLesson.get("seconds") || 0).countdownSeconds(),
- activeLesson: b ? b.toJSON() : null,
- badge: this.badges.get(this.lesson.get("badge") || 1).get("template")
- })), this
- }
- })
- }), define("lessons/views/screen_complete", ["require", "templates", "registry", "shared/scoring"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/scoring");
- return Backbone.View.extend({
- saved: !1,
- events: {
- "click .continue-button": "continue",
- "click .try-again": "restart",
- "click .saving-failed a": "saveTryAgain"
- },
- initialize: function(a) {
- void 0 === this.template && (this.template = b("lessons", "screen_complete")), this.user = c.get("user"), this.screen = a.screen, this.screenType = a.screenType, this.userLesson = a.userLesson, this.userLessonScreens = a.userLessonScreens, this.lesson = a.lesson, this.lettersTyped = a.lettersTyped, this.input = a.input, this.typingView = a.typingView, this.problemKeysLesson = a.problemKeysLesson
- },
- render: function() {
- this.listenTo(this.input, "keypress", this.handleKeyPress);
- var a = d.accuracy(this.screen.get("typed"), this.screen.get("errors")),
- b = 0,
- c = 0,
- e = 0;
- this.userLesson.get("progress") > 0 && (b = d.accuracy(this.userLesson.get("typed") + this.screen.get("typed"), this.userLesson.get("errors") + this.screen.get("errors")), c = d.speed(this.userLesson.get("typed") + this.screen.get("typed"), this.userLesson.get("seconds") + this.screen.get("seconds"), this.userLesson.get("errors") + this.screen.get("errors")), e = (this.userLesson.get("seconds") + this.screen.get("seconds") || 0).countdownSeconds()), this.$el.html(this.template({
- loggedIn: FTWGLOBALS("loggedIn"),
- lesson: this.lesson.toJSON(),
- two_stars: this.user.get("two_stars"),
- three_stars: this.user.get("three_stars"),
- stars: d.stars(a, this.user.get("two_stars"), this.user.get("three_stars")),
- accuracy: 0,
- speed: 0,
- problemKeys: _.pluck(this.lettersTyped.topProblemKeys(3), "id"),
- time: 0,
- lessonAccuracy: b,
- lessonSpeed: c,
- lessonTime: e,
- screenType: this.screenType,
- problemKeysLesson: this.problemKeysLesson,
- lineSpeeds: this.calculateLineSpeeds()
- }));
- var f = this.$(".stars span").css({
- visibility: "hidden"
- }),
- g = "easeOutQuint",
- h = 200,
- i = 400,
- j = 1e3;
- $.Velocity(f[0], {
- opacity: [1, 0],
- rotateZ: ["-5deg", 0],
- scale: [1, 15]
- }, {
- delay: h + i,
- display: "inline-block",
- visibility: "visible",
- duration: j,
- easing: g
- }), $.Velocity(f[1], {
- opacity: [1, 0],
- scale: [1.2, 15]
- }, {
- delay: 2 * h + i,
- display: "inline-block",
- visibility: "visible",
- duration: j,
- easing: g
- }), $.Velocity(f[2], {
- opacity: [1, 0],
- rotateZ: ["5deg", 0],
- scale: [1, 15]
- }, {
- delay: 3 * h + i,
- display: "inline-block",
- visibility: "visible",
- duration: j,
- easing: g
- });
- var k = function() {
- this.$("#screen-time").countdown([this.screen.get("seconds") || 0, 0], {
- formatter: function(a) {
- return a.countdownSeconds()
- }
- }, 500)
- }.bind(this),
- l = function() {
- this.$("#screen-accuracy").countdown([a, 0], null, 500, k)
- }.bind(this);
- return this.$("#screen-speed").countdown([d.speed(this.screen.get("typed"), this.screen.get("seconds"), this.screen.get("errors")), 0], null, 500, l), this
- },
- "continue": function() {
- return this.saved ? (this.$(".continue-button").addClass("pending"), this.trigger("continue"), location.softReload(), !1) : !1
- },
- restart: function() {
- return this.saved && this.userLesson.inc({
- progress: -1
- }), location.href = location.href, !1
- },
- handleKeyPress: function(a) {
- "\n" == a.key && this["continue"]()
- },
- showSaved: function() {
- this.saved = !0, this.$(".saving-spinner").hide(), this.$(".saving-failed").hide(), this.$(".continue-button").show()
- },
- showSaveError: function() {
- this.$(".saving-spinner").hide(), this.$(".saving-failed").show()
- },
- saveTryAgain: function() {
- return this.$(".saving-failed").hide(), this.$(".saving-spinner").show(), this.trigger("try-again"), !1
- },
- calculateLineSpeeds: function() {
- var a = [];
- return "speed" == this.screenType && this.typingView.lineScores.forEach(function(b, c) {
- var e = d.speed(b.typed, b.seconds, b.errors),
- f = 0;
- c > 0 && (f = d.speed(this.typingView.lineScores[c - 1].typed, this.typingView.lineScores[c - 1].seconds, this.typingView.lineScores[c - 1].errors)), a[c] = {
- speed: e,
- improvement: f > 0 ? Math.round((e - f) / f * 100) : 0
- }
- }.bind(this)), a
- }
- })
- }), define("lessons/views/screen_failed", ["require", "templates", "shared/scoring"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/scoring");
- return Backbone.View.extend({
- saved: !1,
- events: {
- "click .continue-button": "restart"
- },
- initialize: function(a) {
- void 0 === this.template && (this.template = b("lessons", "screen_failed")), this.screen = a.screen, this.screenType = a.screenType, this.userLesson = a.userLesson, this.userLessonScreens = a.userLessonScreens, this.lesson = a.lesson, this.lettersTyped = a.lettersTyped, this.input = a.input, this.typingView = a.typingView
- },
- render: function() {
- this.listenTo(this.input, "keypress", this.handleKeyPress);
- var a = c.accuracy(this.screen.get("typed"), this.screen.get("errors"));
- return this.$el.html(this.template({
- lesson: this.lesson.toJSON(),
- accuracy: a,
- speed: c.speed(this.screen.get("typed"), this.screen.get("seconds"), this.screen.get("errors")),
- problemKeys: _.pluck(this.lettersTyped.topProblemKeys(3), "id"),
- time: (parseInt(this.screen.get("seconds")) || 0).countdownSeconds(),
- screenType: this.screenType
- })), this
- },
- restart: function() {
- return location.softReload(), !1
- },
- handleKeyPress: function(a) {
- "\n" == a.key && this.restart()
- }
- })
- }), define("test/views/name_modal", ["require", "templates", "shared/form_model", "registry", "shared/form_validation"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/form_model"),
- d = a("registry"),
- e = a("shared/form_validation"),
- f = c.extend({
- url: "/apiv1/student/user/account",
- defaults: {
- first_name: "",
- last_name: ""
- },
- validationRules: {
- first_name: {
- required: !0
- },
- last_name: {
- required: !0
- }
- },
- validationMessages: {}
- });
- return Backbone.View.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- baseModel: null,
- initialize: function(a) {
- this.options = a, this.template = b("test", "name_modal"), this.model = new f
- },
- render: function() {
- return this.$el.html(this.template({})), this.form = this.$("form"), new e(this.form, this.button, this.model, this.successCallback, this), this
- },
- ok: function() {
- this.form.submit()
- },
- successCallback: function(a) {
- d.get("user").set(a), this.trigger("complete")
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("lessons/views/test_complete", ["require", "templates", "shared/scoring", "registry", "global/views/notice", "global/views/modal", "test/views/name_modal", "lessons/views/screen_complete"], function(a) {
- "use strict";
- var b = a("templates"),
- c = (a("shared/scoring"), a("registry")),
- d = a("global/views/notice"),
- e = a("global/views/modal"),
- f = a("test/views/name_modal"),
- g = a("lessons/views/screen_complete");
- return g.extend({
- initialize: function(a) {
- g.prototype.initialize.apply(this, arguments), this.template = b("lessons", "test_complete"), this.user = c.get("user")
- },
- render: function() {
- return g.prototype.render.apply(this, arguments), this
- },
- "continue": function() {
- if (!this.saved) return !1;
- var a = this.userLesson.get("last_save_response").id;
- return this.user.get("first_name") || this.user.get("last_name") ? window.open("/apiv1/student/tests/" + a + "/certificate") : this.requestName(), !1
- },
- restart: function() {
- return location.softReload(), !1
- },
- requestName: function() {
- var a = new e({
- title: "Enter Your Name",
- okButton: "Continue",
- cancelButton: "Cancel",
- contentView: new f,
- width: 500
- });
- return a.on("complete", function() {
- new d({
- error: !1,
- text: "Your name has been updated. You may now print your certificate."
- }).show()
- }.bind(this)), a.show(), !1
- },
- handleKeyPress: function(a) {
- return !1
- }
- })
- }), define("lessons/views/progress", ["require", "templates", "shared/scoring"], function(a) {
- "use strict";
- var b = 200,
- c = 20,
- d = a("templates"),
- e = a("shared/scoring");
- return Backbone.View.extend({
- events: {
- "mouseover .progress-screens span": "showDetails",
- "mouseout .progress-screens span": "hideDetails",
- "click .progress-screens span": "goToScreen"
- },
- initialize: function(a) {
- void 0 === this.template && (this.template = d("lessons", "progress"), this.hoverTemplate = d("lessons", "progress_hover")), this.userLesson = a.userLesson, this.userLessonScreens = a.userLessonScreens, this.lesson = a.lesson, this.screens = a.screens
- },
- render: function() {
- return this.$el.html(this.template({
- lesson: this.lesson.toJSON(),
- screens: this.screens.toJSON(),
- userLesson: this.userLesson.toJSON(),
- userLessonScreens: this.userLessonScreens
- })), this.hover = this.$("#progressHover"), this
- },
- goToScreen: function(a) {
- var b = $(a.currentTarget),
- c = b.data("id"),
- d = this.screens.indexOf(this.screens.get(c));
- d <= this.userLessonScreens.length && (this.userLesson.set({
- progress: d
- }), location.softReload())
- },
- showDetails: function(a) {
- var d = $(a.currentTarget),
- f = d.data("id"),
- g = d.width(),
- h = d.position().left,
- i = this.screens.get(f),
- j = this.userLessonScreens.get(f),
- k = 0,
- l = 0,
- m = 0,
- n = !1;
- j && (k = j.get("stars"), l = e.speed(j.get("typed"), j.get("seconds"), j.get("errors")), m = e.accuracy(j.get("typed"), j.get("errors")), n = !0), this.hover.css({
- left: h + g / 2 - b / 2 + c - 4
- }).html(this.hoverTemplate({
- title: i.get("title"),
- stars: k,
- speed: l,
- accuracy: m,
- completed: n
- })).velocity("stop", !0).velocity("ftw.perspectiveShake", {
- display: "block"
- })
- },
- hideDetails: function(a) {
- return $(a.relatedTarget).parent().hasClass("progress-screens") ? !1 : (this.hover.fastHide(), !1)
- }
- })
- }), define("shared/sounds", ["require"], function(a) {
- "use strict";
- return {
- sounds: {},
- play: function(a) {
- "string" == typeof a && (a = {
- id: a
- });
- try {} catch (b) {}
- a.loop = a.loop || !1, a.volume = a.volume || 1, a.delay = a.delay || 0, this.sounds[a.id] && (this.sounds[a.id].volume = a.volume, this.sounds[a.id].loop = a.loop, setTimeout(function() {
- try {
- this.sounds[a.id].play()
- } catch (b) {}
- }.bind(this), a.delay))
- },
- stop: function(a, b) {
- var c = this.sounds[a];
- c && (c.pause(), c.currentTime = 0)
- },
- preload: function(a, b, c) {
- b = b || "/dist/site/images/sounds/", c = c || a;
- try {
- this.sounds[a] = new Audio(b + c + ".mp3")
- } catch (d) {}
- }
- }
- }), define("shared/tooltip", ["require", "templates"], function(a) {
- "use strict";
- var b = (a("templates"), Backbone.View.extend({
- className: "tooltip",
- initialize: function() {
- $("body").append(this.$el)
- },
- init: function() {
- $(".tooltip-trigger").off("mouseover.tooltip").off("mouseout.tooltip").on("mouseover.tooltip", function(a) {
- this.show(a.currentTarget, $(a.currentTarget).data("tooltip"))
- }.bind(this)).on("mouseout.tooltip", function() {
- this.hide()
- }.bind(this))
- },
- show: function(a, b) {
- a = $(a), this.$el.html(b).css({
- left: a.offset().left - this.$el.outerWidth() / 2 + a.outerWidth() / 2 - 3,
- top: a.offset().top - this.$el.outerHeight() - 10
- }), this.$el.velocity("ftw.growUp", {
- display: "block"
- })
- },
- hide: function() {
- this.$el.fastHide()
- }
- }));
- return new b
- }), define("lessons/views/typing", ["require", "templates", "registry", "shared/sounds", "shared/scoring", "shared/tooltip", "global/views/keyboard"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/sounds"),
- e = (a("shared/scoring"), a("shared/tooltip")),
- f = a("global/views/keyboard");
- return Backbone.View.extend({
- keyboardType: null,
- templateName: null,
- lettersCache: null,
- activeLetter: null,
- events: {
- "click .button-restart": "restart"
- },
- initialize: function(a) {
- void 0 === this.template && (this.baseTemplate = b("lessons", "typing"), this.template = b("lessons", this.templateName)), this.lettersTyped = a.lettersTyped, this.user = c.get("user"), this.input = a.input, this.timer = a.timer, this.keyboardType && (this.keyboard = new f({
- type: this.keyboardType,
- model: a.keyboard
- }), this.input.on("capslock", this.keyboard.toggleCapsLockWarning.bind(this.keyboard))), d.preload("error"), this.userLesson = a.userLesson, this.userLessonScreens = a.userLessonScreens, this.lesson = a.lesson, this.screens = a.screens, this.screen = a.screen, this.screen.on("change:typed", this.handleKeyTyped, this), this.screen.on("change:errors", this.handleErrorTyped, this), this.screen.once("complete", this.handleComplete.bind(this))
- },
- render: function() {
- return e.hide(), this.$el.html(this.baseTemplate()), this.listenTo(this.input, "keypress", this.handleInput), this.keyboard && (this.$("#keyboard-content").append(this.keyboard.render().el), this.keyboard.highlightKey(this.screen.charAt(0))), c.get("isMobile") && "software" == this.user.get("_mobileKeyboard") && $("#ios-overlay").fastShow(), this.setPosition(0), this
- },
- handleComplete: function() {
- this.screen.off("complete", this.handleComplete.bind(this)), this.timer.stop();
- var a = -1;
- this.keyTimes.length > 20 && (a = Math.round(this.std(this.keyTimes))), this.screen.set({
- seconds: this.timer.get("seconds"),
- std: a
- }), this.trigger("complete"), e.init()
- },
- startCursorAnimation: function() {
- c.get("userActivity").length <= 1 && 0 === this.userLesson.get("progress") && (this.startCursorTimeout = setTimeout(function() {
- e.show(this.$(".letter.active"), "Start Typing!")
- }.bind(this), 3e3))
- },
- stopCursorAnimation: function() {
- clearTimeout(this.startCursorTimeout), e.hide()
- },
- keyTimes: [],
- lastKeyTime: 0,
- handleInput: function(a) {
- if (this.lastKeyTime) {
- var b = Date.now();
- this.keyTimes.push(Date.now() - this.lastKeyTime), this.lastKeyTime = b
- }
- 0 === this.screen.get("typed") && (this.timer.start(), this.lastKeyTime = Date.now(), this.stopCursorAnimation()), "NEXT" == a.key ? (this.timer.stop().set({
- seconds: 10
- }), this.screen.quickComplete()) : this.screen.handleInput(a)
- },
- handleKeyTyped: function(a) {
- a.changed.lastKeyError ? this.lettersTyped.trackError(a.get("correctLetter")) : this.lettersTyped.trackCorrect(a.get("letterTyped")), this.keyboard && this.keyboard.highlightKey(a.get("nextKey")), this.setPosition(a.changed.typed, a.get("lastKeyError"))
- },
- restart: function() {
- return location.softReload(), !1
- },
- handleErrorTyped: function(a) {
- if (this.activeLetter) {
- var b = $("<div>").addClass("error-letter").css({
- color: "red"
- }).html(a.get("letterTyped"));
- this.$(this.activeLetter).prev().append(b), b.velocity("transition.slideDownOut")
- }
- d.play("error")
- },
- setPosition: function(a, b) {
- if (!this.lettersCache) {
- var c = this.$(".screen span");
- if (0 === c.length) return;
- this.lettersCache = c
- }
- var d = a - 1 >= 0 ? this.lettersCache[a - 1] : null,
- e = this.lettersCache[a] || null,
- f = this.lettersCache.length > a + 1 ? this.lettersCache[a + 1] : null;
- e && (e.classList.add("active"), e.classList.remove("error"), e.classList.remove("typed"), this.activeLetter = e), f && (f.classList.remove("error"), f.classList.remove("typed"), f.classList.remove("active")), d && a > 0 && (d.classList.remove("active"), d.classList.add("typed"), b && d.classList.add("error"))
- },
- hide: function() {
- this.stopListening(this.input), this.remove()
- },
- std: function(a) {
- a.sort(function(a, b) {
- return a - b
- }), a.pop(), a.pop(), a.pop(), a.pop(), a.shift(), a.shift(), a.shift(), a.shift();
- var b = function(a) {
- var b = a.reduce(function(a, b) {
- return a + b
- }, 0),
- c = b / a.length;
- return c
- },
- c = b(a),
- d = a.map(function(a) {
- var b = a - c,
- d = b * b;
- return d
- }),
- e = b(d),
- f = Math.sqrt(e);
- return f
- }
- })
- }), define("lessons/views/typing_standard", ["require", "templates", "registry", "lessons/views/typing"], function(a) {
- "use strict";
- var b = (a("templates"), a("registry")),
- c = a("lessons/views/typing");
- return c.extend({
- keyboardType: "small",
- templateName: "typing_standard",
- initialize: function(a) {
- return c.prototype.initialize.call(this, a)
- },
- render: function() {
- return c.prototype.render.call(this), this.$("#lesson-content").append(this.template({
- minimumLines: 4,
- lesson: this.lesson.toJSON(),
- typingContent: this.screen.getContentByLine(),
- user: b.get("user").toJSON()
- })), this.startCursorAnimation(), this
- }
- })
- }), define("lessons/views/typing_falling", ["require", "templates", "registry", "lessons/views/typing"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("lessons/views/typing"),
- e = 52,
- f = 40,
- g = 10,
- h = 270,
- i = -20,
- j = 700,
- k = 70,
- l = 880;
- return d.extend({
- keyboardType: "large",
- templateName: "typing_falling",
- initialize: function(a) {
- return this.lettersTemplate = b("lessons", "typing_falling_letters"), this.lessonLength = a.screen.get("content").length, d.prototype.initialize.call(this, a)
- },
- render: function() {
- return d.prototype.render.call(this), this.$("#lesson-content").append(this.template({
- lesson: this.lesson.toJSON(),
- user: c.get("user").toJSON()
- })), setTimeout(this.renderLetters.bind(this), 0), this
- },
- renderLetters: function() {
- var a, b = {},
- c = this.$(".keyboard"),
- d = Math.ceil(this.$(".screen").css("border-left-width").replace("px", "")) || 0;
- j -= 2 * d, l -= 2 * d, i -= d, k -= d;
- var m = $("body").hasClass("compact"),
- n = m ? j : l;
- this.screen.get("content").split("").forEach(function(d) {
- d = d.toLowerCase(), a = d.charCodeAt(0), "âŽ" == d && (a = 13), b[a] || (b[a] = Math.min(n - g, c.find(".key-" + a).position().left))
- }.bind(this)), this.$(".screen").html(this.lettersTemplate({
- lettersPos: b,
- typingContent: this.screen.get("content"),
- rowHeight: e,
- letterPadding: g,
- letterWidth: f,
- rowWidth: m ? j : l,
- viewportHeight: h,
- viewportLeftOffset: m ? i : k
- })), this.lettersContent = this.$("#lettersContent"), this.progressMeter = this.$("#screenProgress"), this.setPosition(0)
- },
- handleErrorTyped: function(a) {
- this.lettersTyped.trackError(a.get("correctLetter")), this.setPosition(a.get("typed"), !0), d.prototype.handleErrorTyped.call(this, a)
- },
- setPosition: function(a, b) {
- if (!this.lettersCache) {
- var c = this.$("#lettersContent .letter-line");
- if (0 === c.length) return;
- this.lettersCache = c, this.keysCache = this.$("#lettersContent .key-letter");
- }
- var d = this.lessonLength - a - 1,
- e = this.lettersCache[d],
- f = this.keysCache[d],
- g = this.lettersCache[d + 1];
- return b && e ? (e.classList.add("error"), void $.Velocity(f, "ftw.miniShake", {
- duration: 300
- })) : (e && e.classList.add("active"), g && (g.classList.remove("active"), g.classList.add("complete")), void(a > 0 && (this.animate(a), this.updateProgress(a))))
- },
- updateProgress: function(a) {
- var b = Math.round(a / this.lessonLength * 100);
- $(this.progressObject).stop().animate({
- progress: b
- }, {
- step: function(a) {
- this.progressMeter.css({
- "background-size": a + "% 100%"
- })
- }.bind(this)
- }), this.progressObject.progress = b
- },
- progressObject: {
- progress: 0
- },
- animate: function(a) {
- var b = this.lessonLength - a,
- c = e * b - h;
- this.lettersContent.velocity("stop").velocity({
- top: -1 * c
- })
- }
- })
- }), define("lessons/views/typing_speed", ["require", "templates", "shared/scoring", "registry", "lessons/views/typing"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/scoring"),
- d = a("registry"),
- e = a("lessons/views/typing");
- return e.extend({
- keyboardType: "small",
- templateName: "typing_speed",
- initialize: function(a) {
- this.statsTemplate = b("lessons", "typing_speed_stats"), this.currentLine = 0, e.prototype.initialize.call(this, a), this.listenTo(this.screen, "change:line", this.completeLine)
- },
- render: function() {
- return e.prototype.render.call(this), this.$("#lesson-content").append(this.template({
- minimumLines: 4,
- lesson: this.lesson.toJSON(),
- typingContent: this.screen.getContentByLine(),
- user: d.get("user").toJSON()
- })), this.lines = this.$(".line"), this.lineScores = [], this.startCursorAnimation(), this
- },
- completeLine: function() {
- var a, b, d = "";
- 0 === this.currentLine ? a = {
- typed: this.screen.get("typed"),
- seconds: this.timer.get("seconds"),
- errors: this.screen.get("errors"),
- totalSeconds: this.timer.get("seconds"),
- totalErrors: this.screen.get("errors")
- } : (b = this.lineScores[this.currentLine - 1], a = {
- typed: b.typed,
- seconds: this.timer.get("seconds") - b.totalSeconds,
- errors: this.screen.get("errors") - b.totalErrors,
- totalSeconds: this.timer.get("seconds"),
- totalErrors: this.screen.get("errors")
- }, c.speed(a.typed, a.seconds, a.errors) > c.speed(b.typed, b.seconds, b.errors) ? d = "up" : c.speed(a.typed, a.seconds, a.errors) < c.speed(b.typed, b.seconds, b.errors) && (d = "down")), this.lineScores[this.currentLine] = a;
- var e = c.speed(a.typed, a.seconds, a.errors),
- f = 0;
- this.currentLine > 0 && (f = c.speed(this.lineScores[this.currentLine - 1].typed, this.lineScores[this.currentLine - 1].seconds, this.lineScores[this.currentLine - 1].errors)), $(this.lines[this.currentLine]).addClass("complete " + d).find(".repeat-stats").html(this.statsTemplate({
- speed: e,
- improvement: Math.round((e - f) / f * 100)
- })).velocity("fadeIn"), this.currentLine++
- }
- })
- }), define("lessons/views/typing_test", ["require", "templates", "registry", "lessons/views/typing"], function(a) {
- "use strict";
- var b = (a("templates"), a("registry")),
- c = a("lessons/views/typing"),
- d = 50;
- return c.extend({
- keyboardType: !1,
- templateName: "typing_test",
- initialize: function(a) {
- this.options = a, this.options.problemKeysLesson && (this.keyboardType = "small"), c.prototype.initialize.call(this, a), this.listenTo(this.screen, "change:line", this.animate), this.listenTo(this.timer, "change:startTime", this.beginProgress)
- },
- render: function() {
- return c.prototype.render.call(this), this.$("#lesson-content").append(this.template({
- lesson: this.lesson.toJSON(),
- typingContent: this.screen.getContentByLine(),
- minutes: this.options.minutes,
- user: b.get("user").toJSON()
- })), this.typingContent = this.$("#typingContent"), this.progressMeter = this.$("#screenProgress"), this.startCursorAnimation(), this
- },
- handleComplete: function() {
- return this.timer.stop(), this.timer.set({
- seconds: 60 * this.options.minutes
- }), c.prototype.handleComplete.call(this)
- },
- beginProgress: function() {
- var a = 60 * this.options.minutes,
- b = function(b) {
- return (a - Math.floor(b / 100 * a)).countdownSeconds()
- };
- $({
- progress: 0
- }).animate({
- progress: 100
- }, {
- step: function(a) {
- this.progressMeter.html("<span>" + b(a) + "</span>").css({
- "background-size": a + "% 100%"
- })
- }.bind(this),
- easing: "linear",
- duration: 60 * this.options.minutes * 1e3,
- complete: this.handleComplete.bind(this)
- })
- },
- animate: function(a, b) {
- this.typingContent.velocity("stop").velocity({
- top: -1 * (d * b)
- }, "slow", "linear")
- }
- })
- }), define("shared/keyboard_input", ["require", "registry"], function(a) {
- "use strict";
- var b = a("registry"),
- c = function() {};
- return _.extend(c.prototype, Backbone.Events, {
- boundElement: null,
- capsLockWarningOn: !1,
- initialize: function() {
- b.get("isMobile") && "software" == b.get("user").get("_mobileKeyboard") && (this.boundElement = $("#mobile-input").fastShow(), this.boundElement.on("focus", this.hideMobileFocus.bind(this)), this.boundElement.on("blur", this.showMobileFocus.bind(this)), this.trigger("input-blur")), $(document).on("keypress", this.boundElement, _.bind(this.handleKeyPress, this)), $(document).on("keydown", this.boundElement, _.bind(this.handleKeyDown, this)), $(document).on("blur", this.boundElement, _.bind(this.handleBlur, this)), $(document).on("focus", this.boundElement, _.bind(this.handleFocus, this))
- },
- showMobileFocus: function() {
- $("#ios-overlay").velocity("fadeIn")
- },
- hideMobileFocus: function() {
- $("#ios-overlay").fastHide()
- },
- stop: function() {
- $(document).off("keypress keydown blur focus")
- },
- prevKeyPress: null,
- handleKeyPress: function(a) {
- if (this.prevKeyPress && this.prevKeyPress.timeStamp === a.timeStamp) return !1;
- this.prevKeyPress = a;
- var b = null;
- if (a.target.form && ("contactForm" == a.target.form.id || "nameForm" == a.target.form.id)) return !0;
- switch (a.which = a.which || a.keyCode, a.which) {
- case 13:
- b = "\n"
- }
- return b || (b = String.fromCharCode(a.which)), !a.shiftKey && b.match(/[a-zA-Z]/) && (b !== b.toUpperCase() || this.capsLockWarningOn ? b !== b.toUpperCase() && this.capsLockWarningOn && (this.trigger("capslock", !1), this.capsLockWarningOn = !1) : (this.trigger("capslock", !0), this.capsLockWarningOn = !0)), this.trigger("keypress", {
- keyCode: a.which,
- key: b,
- special: !1
- }), !1
- },
- prevKeyDown: null,
- handleKeyDown: function(a) {
- if (this.prevKeyDown && this.prevKeyDown.timeStamp === a.timeStamp) return !1;
- if (this.prevKeyDown = a, a.target.form && ("contactForm" == a.target.form.id || "nameForm" == a.target.form.id)) return !0;
- var b = null;
- switch (a.which = a.which || a.keyCode, a.which) {
- case 9:
- b = "TAB";
- break;
- case 20:
- b = "CAPSLOCK";
- break;
- case 91:
- b = "CMD";
- break;
- case 18:
- b = "ALT";
- break;
- case 16:
- b = "SHIFT";
- break;
- case 17:
- b = "CTRL";
- break;
- case 8:
- b = "BACKSPACE";
- break;
- case 46:
- b = "DELETE";
- break;
- case 12:
- b = "NUMLOCK";
- break;
- case 27:
- b = "ESC";
- break;
- case 32:
- return this.handleKeyPress(a)
- }
- return "CAPSLOCK" !== b || this.capsLockWarningOn || (this.trigger("capslock", !0), this.capsLockWarningOn = !0), 192 == a.which && a.shiftKey && a.ctrlKey && (b = "NEXT"), null !== b ? (this.trigger("keypress", {
- keyCode: a.which,
- key: b,
- special: !0
- }), !1) : void 0
- },
- handleFocus: function(a) {
- this.trigger("focus", a)
- },
- handleBlur: function(a) {
- this.trigger("blur", a)
- }
- }), c
- }), define("global/models/timer", ["require"], function(a) {
- "use strict";
- return Backbone.Model.extend({
- "default": {
- startTime: 0,
- seconds: 0
- },
- initialize: function() {},
- start: function(a, b) {
- return this.get("startTime") ? this : (this.set({
- startTime: this.getTime(b),
- pauseTime: 0
- }), this.interval = setInterval(this._updateSeconds.bind(this), 100), this)
- },
- pause: function() {
- return this.set({
- pauseTime: this.getTime()
- }), this
- },
- unpause: function() {
- return this.inc({
- startTime: this.getTime() - this.get("pauseTime")
- }), this.set({
- pauseTime: 0
- }), this
- },
- stop: function() {
- return clearInterval(this.interval), this
- },
- getTime: function(a) {
- return a = a || new Date, a.getTime() / 1e3
- },
- _updateSeconds: function() {
- var a = this.get("pauseTime") || this.getTime();
- this.set({
- seconds: a - this.get("startTime")
- })
- }
- })
- }), define("global/collections/keyboards", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "keyboard_id"
- });
- return Backbone.Collection.extend({
- model: b,
- comparator: "sort_order"
- })
- }), define("global/collections/letters_typed", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- defaults: {
- id: "",
- typed: 0,
- errors: 0
- }
- }),
- c = Backbone.Collection.extend({
- model: b,
- trackCorrect: function(a) {
- if (this.valid(a)) {
- a = a.toLowerCase();
- var b = this.get(a);
- b ? b.inc({
- typed: 1
- }) : this.add({
- id: a,
- typed: 1
- })
- }
- },
- trackError: function(a) {
- if (this.valid(a)) {
- a = a.toLowerCase();
- var b = this.get(a);
- b ? b.inc({
- errors: 1
- }) : this.add({
- id: a,
- errors: 1
- })
- }
- this.trackCorrect(a)
- },
- valid: function(a) {
- return a ? a.length > 1 ? !1 : "" === $.trim(a) ? !1 : !!a.match(/[A-Za-z]/) : void 0
- },
- topProblemKeys: function(a) {
- a = a || 4;
- var b = this.map(function(a) {
- return {
- id: a.id,
- percent: a.attributes.errors / a.attributes.typed
- }
- });
- return _.chain(b).sortBy(function(a) {
- return -1 * a.percent
- }).filter(function(a) {
- return a.percent > 0
- }).sortBy(function(a) {
- return -a.percent
- }).first(a).value()
- },
- merge: function(a) {
- var b;
- a.forEach(function(a) {
- b = this.get(a.id), b ? b.inc({
- typed: a.typed,
- errors: a.errors
- }) : this.add(a)
- }.bind(this))
- },
- clear: function() {
- this.reset(), $.postJSON("/apiv1/student/user/reset-problem-keys")
- }
- });
- return c
- }), define("lessons/views/mobile_keyboard_modal", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- events: {
- "click .keyboard-button": "clickButton"
- },
- initialize: function(a) {
- this.template = b("lessons", "mobile_keyboard_modal")
- },
- render: function() {
- return this.$el.html(this.template({})), this
- },
- clickButton: function(a) {
- $(a.currentTarget).hasClass("software") ? this.trigger("complete", "software") : this.trigger("complete", "hardware")
- }
- })
- }), define("lessons/index", ["require", "templates", "registry", "global/views/modal", "shared/scoring", "shared/radar_tracker", "global/models/lesson", "lessons/collections/screens", "lessons/collections/problemkey_screens", "lessons/views/nav", "lessons/views/intro", "lessons/views/congrats", "lessons/views/screen_complete", "lessons/views/screen_failed", "lessons/views/test_complete", "lessons/views/progress", "lessons/views/typing_standard", "lessons/views/typing_falling", "lessons/views/typing_speed", "lessons/views/typing_test", "shared/keyboard_input", "global/models/timer", "shared/tooltip", "global/collections/keyboards", "global/collections/user_lesson_screens", "global/collections/letters_typed", "lessons/views/mobile_keyboard_modal", "shared/model_backends/localstorage"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("global/views/modal"),
- e = a("shared/scoring"),
- f = a("shared/radar_tracker"),
- g = a("global/models/lesson"),
- h = a("lessons/collections/screens"),
- i = a("lessons/collections/problemkey_screens"),
- j = a("lessons/views/nav"),
- k = a("lessons/views/intro"),
- l = a("lessons/views/congrats"),
- m = a("lessons/views/screen_complete"),
- n = a("lessons/views/screen_failed"),
- o = a("lessons/views/test_complete"),
- p = a("lessons/views/progress"),
- q = a("lessons/views/typing_standard"),
- r = a("lessons/views/typing_falling"),
- s = a("lessons/views/typing_speed"),
- t = a("lessons/views/typing_test"),
- u = a("shared/keyboard_input"),
- v = a("global/models/timer"),
- w = a("shared/tooltip"),
- x = a("global/collections/keyboards"),
- y = a("global/collections/user_lesson_screens"),
- z = a("global/collections/letters_typed"),
- A = a("lessons/views/mobile_keyboard_modal"),
- B = a("shared/model_backends/localstorage");
- return Backbone.View.extend({
- el: "#js-content",
- events: {},
- initialize: function(a) {
- this.options = a, void 0 === this.template && (this.template = b("lessons", "index")), this.screenTypes = {
- standard: q,
- speed: s,
- falling: r,
- test: t
- }, this.user = c.get("user"), this.userBadges = c.get("userBadges"), this.userLettersTyped = c.get("userLettersTyped"), this.userActivity = c.get("userActivity"), this.userTests = c.get("userTests"), this.input = new u, this.timer = new v, this.lesson = new g(FTWGLOBALS("lesson"));
- var d = new x(FTWKEYBOARDS),
- e = 380 == this.lesson.id || 351 == this.lesson.id ? 29 : this.user.get("keyboard_id");
- if (this.keyboard = d.get(e), this.options.problemkeys) this.options.test = !0, this.options.minutes = 1, this.screens = new i(FTWGLOBALS("screens"), {
- words: FTWGLOBALS("words"),
- letters: this.userLettersTyped.topProblemKeys(3)
- });
- else if (this.options.test) {
- this.lesson.set({
- screens: 1
- });
- var w = FTWGLOBALS("screens")[0];
- w.title = this.options.minutes + ":00 Typing Test", w.content = _.shuffle(FTWGLOBALS("screens")).map(function(a) {
- return a.content.replace(/\n+/g, "\n").trim()
- }).join("\n").substr(0, 750 * this.options.minutes), this.screens = new h([w])
- } else this.screens = new h(FTWGLOBALS("screens"));
- this.lettersTyped = new z;
- var A = c.get("userLessons");
- if (this.userLesson = A.get(this.lesson.id), this.userLesson || (this.userLesson = A.add({
- lesson_id: this.lesson.id
- })), this.options.test && this.userLesson.get("progress") > 0 && this.userLesson.set({
- progress: 0
- }), this.userLessonScreens = new y(null, {
- lesson_id: this.lesson.id
- }), this.userLessonScreens.setBackend(new B("userLessonScreens" + this.lesson.id), "backend"), this.userLessonScreens.length > 0 && this.userLessonScreens.at(0).get("user_id") != this.user.id && this.userLessonScreens.reset(), this.options.test || this.options.problemkeys || document.referrer.match(/\/student\/lessons/) || f.track(c.get("user"), {
- seconds: this.userLesson.get("seconds"),
- errors: this.userLesson.get("errors"),
- typed: this.userLesson.get("typed"),
- progress: this.userLesson.get("progress")
- }), this.screen = this.screens.at(this.userLesson.get("progress")), this.screen) {
- this.screen.formatContent(this.keyboard, this.user), this.screen.get("intro") && (this.intro = new k({
- test: this.options.test && !this.options.problemkeys,
- keyboard: this.keyboard,
- contents: this.screen.get("intro"),
- input: this.input
- }), this.intro.once("begin", this.showScreen.bind(this)));
- var C = this.options.test ? "test" : this.screen.get("screen_type");
- this.typing = new this.screenTypes[C]({
- keyboard: this.keyboard,
- lesson: this.lesson,
- screens: this.screens,
- screen: this.screen,
- input: this.input,
- timer: this.timer,
- userLesson: this.userLesson,
- userLessonScreens: this.userLessonScreens,
- lettersTyped: this.lettersTyped,
- problemKeysLesson: this.options.problemkeys,
- minutes: this.options.minutes
- }), this.typing.once("complete", this.handleScreenComplete.bind(this)), this.options.test ? this.screenComplete = new o({
- lesson: this.lesson,
- screens: this.screens,
- screen: this.screen,
- input: this.input,
- timer: this.timer,
- typingView: this.typing,
- userLesson: this.userLesson,
- userLessonScreens: this.userLessonScreens,
- lettersTyped: this.lettersTyped,
- problemKeysLesson: this.options.problemkeys,
- screenType: C
- }) : this.screenComplete = new m({
- lesson: this.lesson,
- screens: this.screens,
- screen: this.screen,
- input: this.input,
- timer: this.timer,
- typingView: this.typing,
- userLesson: this.userLesson,
- userLessonScreens: this.userLessonScreens,
- lettersTyped: this.lettersTyped,
- screenType: C
- }), this.screenFailed = new n({
- lesson: this.lesson,
- screens: this.screens,
- screen: this.screen,
- input: this.input,
- timer: this.timer,
- typingView: this.typing,
- userLesson: this.userLesson,
- userLessonScreens: this.userLessonScreens,
- lettersTyped: this.lettersTyped,
- screenType: C
- })
- } else this.congrats = new l({
- lesson: this.lesson,
- lettersTyped: this.lettersTyped,
- userLesson: this.userLesson,
- userLessonScreens: this.userLessonScreens
- });
- this.navigation = new j({
- lesson: this.lesson,
- screen: this.screen,
- timer: this.timer,
- userLesson: this.userLesson
- }), this.options.test || (this.progress = new p({
- lesson: this.lesson,
- screens: this.screens,
- screen: this.screen,
- userLesson: this.userLesson,
- userLessonScreens: this.userLessonScreens
- })), this.userLesson.get("progress") > 0 && 0 === this.userLessonScreens.length ? (this.userLessonScreens.once("reset", this.render.bind(this)), this.userLessonScreens.fetch({
- reset: !0
- })) : this.render()
- },
- render: function() {
- var a = $(this.template({
- loading: !1
- }));
- a.find("#typing-nav").append(this.navigation.render().el), this.userLesson.get("progress") == this.lesson.get("screens") ? a.find("#typing-contents").append(this.congrats.render().el) : this.intro ? a.find("#typing-contents").append(this.intro.render().el) : a.find("#typing-contents").append(this.typing.render().el), this.progress && a.find("#typing-progress").append(this.progress.render().el), 0 === this.userLesson.get("progress") && this.user.unset("_mobileKeyboard"), this.$el.html(a), c.get("isMobile") && !this.user.get("_mobileKeyboard") ? this.showKeyboardSelectModal() : this.input.initialize(), w.init()
- },
- showKeyboardSelectModal: function() {
- var a = new d({
- title: "Select Tablet/Mobile Keyboard Type",
- okButton: "",
- cancelButton: "",
- closeButton: !1,
- contentView: new A
- });
- a.show(), a.on("complete", function(a) {
- this.user.set({
- _mobileKeyboard: a
- }), this.input.initialize()
- }.bind(this))
- },
- showScreen: function() {
- this.intro.hide(), this.userLesson.get("progress") == this.lesson.get("screens") ? this.$("#typing-contents").append(this.congrats.render().el) : this.$("#typing-contents").append(this.typing.render().el), w.init()
- },
- handleScreenComplete: function() {
- if (this.typing.hide(), e.accuracy(this.screen.get("typed"), this.screen.get("errors")) < this.lesson.get("min_accuracy")) return void this.$("#typing-contents").append(this.screenFailed.render().el);
- this.$("#typing-contents").append(this.screenComplete.render().el), this.screenStats = {
- lesson_id: this.lesson.id,
- lesson_screen_id: parseInt(this.screen.id),
- seconds: this.screen.get("seconds"),
- errors: this.screen.get("errors"),
- typed: this.screen.get("typed"),
- std: this.screen.get("std"),
- stars: e.stars(e.accuracy(this.screen.get("typed"), this.screen.get("errors")), this.user.get("two_stars"), this.user.get("three_stars")),
- completed: this.userLesson.get("progress") + 1 == this.lesson.get("screens") ? 1 : 0,
- progress: this.userLesson.get("progress") + 1,
- created_at: Date.getUnixTime(),
- test: this.options.test ? 1 : 0,
- now: Math.floor(Date.now() / 1e3)
- };
- var a = this.userLessonScreens.get(parseInt(this.screen.id));
- a && (this.screenStats.prev_seconds = a.get("seconds"), this.screenStats.prev_errors = a.get("errors"), this.screenStats.prev_typed = a.get("typed"), this.screenStats.prev_stars = a.get("stars"), this.screenStats.prev_completed = a.get("completed")), this.saveStats()
- },
- saveStats: function() {
- this.screenComplete.once("try-again", this.saveStats, this), this.userLesson.saveStats(this.screenStats, this.lettersTyped.toJSON()).done(function() {
- this.screenComplete.showSaved(), this.updateLocalStats()
- }.bind(this)).fail(function() {
- this.screenComplete.showSaveError()
- }.bind(this))
- },
- updateLocalStats: function() {
- var a = this.screenStats;
- if (!this.options.problemkeys)
- if (this.options.test) this.userTests.reset();
- else {
- var b = {
- progress: 1,
- typed: a.typed,
- errors: a.errors,
- seconds: a.seconds,
- stars: a.stars,
- completed: a.completed
- },
- c = this.userLessonScreens.get(a.lesson_screen_id);
- if (c && (b.typed -= c.get("typed"), b.seconds -= c.get("seconds"), b.errors -= c.get("errors"), b.stars -= c.get("stars"), b.completed -= c.get("completed")), this.userLesson.inc(b), this.userLesson.set({
- max_progress: Math.max(this.userLesson.get("progress"), this.userLesson.get("max_progress")),
- updated_at: a.created_at
- }), this.userLessonScreens.add(a, {
- merge: !0
- }), FTWGLOBALS("loggedIn") && this.screenStats.completed && !this.userBadges.get(this.lesson.id)) {
- var d = this.userLesson.toJSON();
- d.created_at = Math.round(Date.now() / 1e3), this.userBadges.add(d)
- }
- }
- this.userActivity.merge(this.screenStats), this.userLettersTyped.merge(this.lettersTyped.toJSON())
- }
- })
- }), define("global/views/chart", ["require", "templates"], function(a) {
- "use strict";
- var b = a("templates");
- return Backbone.View.extend({
- options: {
- scaleGridLineColor: "rgba(234,234,234,1)",
- scaleShowLabels: !1,
- pointDotRadius: 8,
- pointDotStrokeWidth: 3,
- pointHitDetectionRadius: 0,
- datasetStrokeWidth: 1,
- bezierCurve: !1,
- showTooltips: !0,
- tooltipFillColor: "#000000",
- tooltipFontColor: "#ffffff",
- showLabels: !1
- },
- dataset: {
- labels: [],
- data: []
- },
- initialize: function(a) {
- void 0 === this.template && (this.template = b("global", "chart")), a.width = a.width || this.width || 548, a.height = a.height || this.height || 100, a.type = (a.type || this.type || "line").ucFirst(), this.setOptions(a)
- },
- setData: function(a, b) {
- b = b || {}, b.data = a, this.dataset = b
- },
- setOptions: function(a) {
- _.extend(this.options, a)
- },
- render: function() {
- if (this.$el.html(this.template({
- options: this.options,
- data: this.dataset.data
- })), this.dataset.data.length > 0) {
- var a = this.$("canvas")[0],
- b = a.getContext("2d"),
- c = _.pluck(this.dataset.data, "label");
- window.devicePixelRatio > 1 && (a.width = this.options.width, a.height = this.options.height, a.style.width = this.options.width / window.devicePixelRatio + "px", a.style.height = this.options.height / window.devicePixelRatio + "px", b.scale(window.devicePixelRatio, window.devicePixelRatio)), new Chart(b)[this.options.type]({
- labels: c,
- datasets: [this.dataset]
- }, this.options)
- }
- }
- })
- }), define("test/views/graph", ["require", "global/views/chart", "templates", "shared/scoring"], function(a) {
- "use strict";
- var b = a("global/views/chart"),
- c = a("templates"),
- d = a("shared/scoring");
- return b.extend({
- sampleData: [],
- initialize: function(a) {
- a = a || {}, this.waitingTemplate = c("test", "graph_waiting"), a.tooltipTemplate = function(a) {
- var b = this.dataset.data[a.index];
- return moment(1e3 * b.created_at).format("lll") + " " + Math.round(b.seconds / 60) + " minute " + b.value + " WPM " + d.accuracy(b) + "% acc"
- }.bind(this), this.renderWaiting(), this.listenTo(this.collection, "reset", this.setData), b.prototype.initialize.call(this, a)
- },
- renderWaiting: function() {
- this.$el.html(this.waitingTemplate())
- },
- setData: function() {
- if (this.collection.length < 2) {
- this.$el.addClass("sample");
- var a = [{
- user_test_id: 6,
- seconds: 60,
- errors: 0,
- typed: 266,
- created_at: 1426181915
- }, {
- user_test_id: 5,
- seconds: 60,
- errors: 10,
- typed: 285,
- created_at: 1421794858
- }, {
- user_test_id: 4,
- seconds: 60,
- errors: 12,
- typed: 330,
- created_at: 1421793538
- }, {
- user_test_id: 3,
- seconds: 60,
- errors: 12,
- typed: 360,
- created_at: 1421791395
- }, {
- user_test_id: 2,
- seconds: 60,
- errors: 0,
- typed: 380,
- created_at: 1421791394
- }];
- a.reverse(), this.collection = new Backbone.Collection(a)
- }
- var c = this.collection.toJSON().reverse(),
- e = {
- fillColor: "transparent",
- strokeColor: "#3295db",
- pointStrokeColor: "#ffffff",
- pointColor: "#3295db"
- };
- c = _.map(c, function(a) {
- return a.date = a.label, a.label = "", a.pointLabel = Math.round(a.seconds / 60), a.value = d.speed(a), a
- }, this), b.prototype.setData.call(this, c, e), this.render()
- }
- })
- }), define("test/views/table", ["require", "templates", "shared/scoring", "global/views/notice", "global/views/modal", "test/views/name_modal", "registry"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/scoring"),
- d = a("global/views/notice"),
- e = a("global/views/modal"),
- f = a("test/views/name_modal"),
- g = a("registry");
- return Backbone.View.extend({
- events: {
- "click .print": "clickPrint"
- },
- initialize: function() {
- this.template = b("test", "table"), this.user = g.get("user"), this.collection.length || !FTWGLOBALS("loggedIn") ? this.render() : (this.renderLoading(), this.collection.on("reset", this.render.bind(this)))
- },
- renderLoading: function() {
- return this.$el.html(this.template({
- loading: !0
- })), this
- },
- render: function() {
- var a = this.collection.toJSON().map(function(a) {
- return a.minutes = Math.round(a.seconds / 60) + " minute" + Math.round(a.seconds / 60).pluralize(), a.speed = c.speed(a.typed, a.seconds, a.errors), a.accuracy = c.accuracy(a.typed, a.errors), a
- });
- return this.$el.html(this.template({
- loading: !1,
- loggedIn: FTWGLOBALS("loggedIn"),
- data: a
- })), this
- },
- clickPrint: function(a) {
- var b = $(a.currentTarget).data("id");
- return this.user.get("first_name") || this.user.get("last_name") ? window.open("/apiv1/student/tests/" + b + "/certificate") : this.requestName(), !1
- },
- requestName: function() {
- var a = new e({
- title: "Enter Your Name",
- okButton: "Continue",
- cancelButton: "Cancel",
- contentView: new f,
- width: 500
- });
- return a.on("complete", function() {
- new d({
- error: !1,
- text: "Your name has been updated. You may now print your certificate."
- }).show()
- }.bind(this)), a.show(), !1
- }
- })
- }), define("test/index", ["require", "templates", "registry", "shared/scoring", "test/views/graph", "test/views/table"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = (a("shared/scoring"), a("test/views/graph")),
- e = a("test/views/table");
- return Backbone.View.extend({
- el: "#js-content",
- initialize: function() {
- this.template = b("test", "index"), this.tests = c.get("userTests"), FTWGLOBALS("loggedIn") || this.tests.reset(), this.table = new e({
- collection: this.tests
- });
- var a = $("body").hasClass("compact") ? 394 : 574;
- this.graph = new d({
- width: a,
- height: 144,
- collection: this.tests
- }), this.render(), FTWGLOBALS("loggedIn") && 0 === this.tests.length ? this.tests.fetch({
- reset: !0
- }) : this.tests.trigger("reset")
- },
- render: function() {
- this.$el.append(this.template()), this.$("#testGraph").html(this.graph.el), this.$("#tableContents").append(this.table.el)
- }
- })
- }), define("games/views/scoreboard", ["require", "templates", "registry"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry");
- return Backbone.View.extend({
- events: {},
- initialize: function(a) {
- this.options = a, void 0 === this.template && (this.template = b("games", "scoreboard")), this.user = c.get("user")
- },
- render: function() {
- return this.$el.html(this.template({
- loggedIn: loggedIn(),
- recent: this.options.recentScores.toJSON(),
- high: this.options.highScores.toJSON()
- })), this
- },
- hide: function() {
- var a = this.$("input[type=checkbox]");
- a.prop("checked") && $.post("/apiv1/student/user/account", {
- migrated: 2
- }), this.user.set({
- migrated: 0
- }), this.$el.velocity("slideUp")
- }
- })
- }), define("games/models/score", ["require"], function(a) {
- "use strict";
- return Backbone.Model.extend({
- initialize: function() {},
- urlRoot: "/apiv1/student/games",
- defaults: {
- game_id: 0,
- score: 0,
- seconds: 0,
- created_at: 0
- },
- saveScore: function() {
- var a = rot47(JSON.stringify(this.toJSON()));
- return $.post(this.urlRoot, {
- data: a
- })
- }
- })
- }), define("games/collections/scores", ["require", "games/models/score"], function(a) {
- "use strict";
- var b = a("games/models/score");
- return Backbone.Collection.extend({
- model: b,
- url: "/apiv1/student/games",
- getScores: function(a) {
- return $.get(this.url + "?id=" + a)
- }
- })
- }), define("global/views/video_ad", ["require"], function(a) {
- "use strict";
- return Backbone.View.extend({
- adDisplayContainer: null,
- adsLoader: null,
- adsRequest: null,
- initialize: function(a) {
- this.options = a, this.game = a.game, this.adDisplayContainer = new google.ima.AdDisplayContainer(this.el, this.game[0]), this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer), this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, this.managerLoaded.bind(this), !1), this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.error.bind(this), !1), this.adsRequest = new google.ima.AdsRequest, this.adsRequest.adTagUrl = a.adUrl, this.adsRequest.linearAdSlotWidth = 640, this.adsRequest.linearAdSlotHeight = 400, this.adsRequest.nonLinearAdSlotWidth = 640, this.adsRequest.nonLinearAdSlotHeight = 150, this.adsLoader.requestAds(this.adsRequest)
- },
- error: function(a) {
- console.error("Video Ad Loading Error"), console.error(a.getError()), this.adsManager && this.adsManager.destroy(), this.trigger("complete")
- },
- managerLoaded: function(a) {
- var b = new google.ima.AdsRenderingSettings;
- b.restoreCustomPlaybackStateOnAdBreakComplete = !0, this.adsManager = a.getAdsManager(this.game[0], b), this.adDisplayContainer.initialize(), this.adsManager.init(this.options.width, this.options.height, google.ima.ViewMode.NORMAL), this.adsManager.start(), this.adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, this.error.bind(this)), this.adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.adEvent.bind(this)), this.adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, this.adEvent.bind(this)), this.adsManager.addEventListener(google.ima.AdEvent.Type.USER_CLOSE, this.adEvent.bind(this)), this.adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, this.adEvent.bind(this)), this.adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, this.adEvent.bind(this)), this.adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, this.adEvent.bind(this))
- },
- adEvent: function(a) {
- switch (a.type) {
- case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:
- case google.ima.AdEvent.Type.COMPLETE:
- case google.ima.AdEvent.Type.SKIPPED:
- case google.ima.AdEvent.Type.USER_CLOSE:
- this.trigger("complete")
- }
- }
- })
- }), define("games/play", ["require", "templates", "registry", "games/views/scoreboard", "games/models/score", "games/collections/scores", "global/views/video_ad"], function(a) {
- "use strict";
- var b = (a("templates"), a("registry")),
- c = a("games/views/scoreboard"),
- d = a("games/models/score"),
- e = a("games/collections/scores"),
- f = a("global/views/video_ad");
- return Backbone.View.extend({
- el: "#js-content",
- initialize: function(a) {
- this.options = a || {}, this.user = b.get("user"), this.highScores = new e, this.recentScores = new e, this.highScores.comparator = function(a) {
- return -a.get("score")
- }, this.scoreboardView = new c({
- el: this.$(".js-scoreboard"),
- highScores: this.highScores,
- recentScores: this.recentScores
- }), window.submitGameScoreboard = this.gameOver.bind(this), loggedIn() && FTWGLOBALS("game").scoreboard ? this.highScores.getScores(FTWGLOBALS("game").game_id).done(this.update.bind(this)) : this.scoreboardView.render(), paid() ? this.startGame() : this.showAd()
- },
- showAd: function() {
- this.ad = new f({
- el: this.$("#ad-content"),
- game: this.$("#game-content"),
- adUrl: FTWGLOBALS("adUrl"),
- width: 916,
- height: 400
- }), this.listenTo(this.ad, "complete", this.startGame)
- },
- startGame: function() {
- var a = FTWGLOBALS("game");
- "javascript" == a.type && (Game.inited || (new Game(a.width, a.height, "/game_files/" + a.folder, this.$("#game")[0]), Game.inited = !0)), this.$("#ad-content").fastHide(), this.$("#game-content").fastShow()
- },
- update: function(a) {
- this.highScores.reset(a.data.high, {
- reset: !0
- }), this.recentScores.reset(a.data.recent, {
- reset: !0
- }), this.scoreboardView.render()
- },
- gameOver: function(a) {
- if (loggedIn() && !(a.seconds <= 0)) {
- a.game_id = FTWGLOBALS("game").game_id, a.created_at = Date.getUnixTime();
- var b = new d(a);
- b.saveScore(), "hard" == a.difficulty && "hard" == a.level && (this.recentScores.unshift(b), this.highScores.add(b), this.highScores.length > 3 && this.highScores.pop(), this.scoreboardView.render())
- }
- }
- })
- }), define("skins/index", ["require", "templates", "registry"], function(a) {
- "use strict";
- var b = (a("templates"), a("registry"));
- return Backbone.View.extend({
- el: "#js-content",
- events: {
- "click .skin": "changeSkin"
- },
- initialize: function() {
- this.user = b.get("user")
- },
- changeSkin: function(a) {
- var b = $(a.currentTarget).data("id"),
- c = $.cookie("tc_skin") || "0";
- return this.user.changeSkin(b), $("body").removeClass("skin" + c).addClass("skin" + b), !1
- }
- })
- }), define("badges/index", ["require", "templates", "registry", "shared/scoring", "global/collections/badges", "global/collections/courses", "global/collections/lessons"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/scoring"),
- e = a("global/collections/badges"),
- f = a("global/collections/courses"),
- g = a("global/collections/lessons");
- return Backbone.View.extend({
- el: "#js-content",
- events: {
- "mouseenter .badge": "hoverBadge",
- "mouseleave .badge": "hoverBadge"
- },
- initialize: function() {
- this.template = b("badges", "index"), this.badges = new e, this.courses = new f(FTWGLOBALS("courses")), this.lessons = new g(FTWGLOBALS("lessons")), this.userBadges = c.get("userBadges"), this.render()
- },
- render: function() {
- return this.$el.html(this.template({
- courses: this.courses.toJSON(),
- lessons: this.lessons.toJSON(),
- userBadges: this.userBadges,
- badges: this.badges,
- scoring: d
- })), this
- },
- hoverBadge: function(a) {
- "mouseenter" == a.type ? $(a.currentTarget).velocity("stop", !0).velocity({
- scale: [1.2, 1]
- }, 100) : $(a.currentTarget).velocity("stop", !0).velocity("reverse", 50)
- }
- })
- }), define("settings/views/info", ["require", "templates", "registry"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry");
- return Backbone.View.extend({
- initialize: function() {
- this.template = b("settings", "info"), this.render()
- },
- render: function() {
- return this.$el.html(this.template({
- user: c.get("user").toJSON()
- })), this
- }
- })
- }), define("settings/views/account", ["require", "templates", "registry", "shared/form_model", "shared/form_validation"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/form_model"),
- e = a("shared/form_validation"),
- f = d.extend({
- url: "/apiv1/student/user/account",
- defaults: {
- email: "",
- first_name: "",
- last_name: "",
- country: "",
- allow_contact: 0,
- spaces: 1,
- speed: "wpm",
- sounds: 1
- },
- validationRules: {
- email: {
- email: !0
- }
- },
- validationMessages: {
- email: {
- email: "Invalid email address",
- INVALID_EMAIL: "Invalid email address",
- EMAIL_IN_USE: "This email is in use by a different account"
- }
- }
- });
- return Backbone.View.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- initialize: function() {
- this.template = b("settings", "account"), this.render(), this.model = new f, this.form = this.$("form");
- var a = this.$("form .submit");
- new e(this.form, a, this.model, this.successCallback, this, !0)
- },
- render: function() {
- return this.$el.html(this.template({
- user: c.get("user").toJSON(),
- countries: FTWGLOBALS("countries")
- })), this
- },
- successCallback: function(a) {
- c.get("user").set(a), this.form.velocity("slideUp", function() {
- this.$(".confirm").slideDown()
- }.bind(this))
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("settings/views/password", ["require", "templates", "registry", "shared/form_model", "shared/form_validation"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/form_model"),
- e = a("shared/form_validation"),
- f = d.extend({
- initialize: function() {
- $.validator.addMethod("notMatchUsername", function(a) {
- return a != c.get("user").get("username")
- }, "Can not match username");
- },
- url: "/apiv1/student/user/password",
- defaults: {
- oldPassword: "",
- password: "",
- password2: ""
- },
- validationRules: {
- oldPassword: {
- required: !0
- },
- password: {
- notMatchUsername: !0,
- required: !0,
- minlength: 4
- },
- password2: {
- required: !0,
- minlength: 4,
- equalTo: "input[name=password]"
- }
- },
- validationMessages: {
- password2: {
- equalTo: "Passwords do not match"
- },
- oldPassword: {
- INCORRECT_PASSWORD: "Incorrect password"
- }
- }
- });
- return Backbone.View.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- initialize: function() {
- this.template = b("settings", "password"), this.render(), this.model = new f, this.form = this.$("form");
- var a = this.$("form .submit");
- new e(this.form, a, this.model, this.successCallback, this, !0)
- },
- render: function() {
- return this.$el.html(this.template()), this
- },
- successCallback: function(a) {
- this.form.velocity("slideUp", function() {
- this.$(".confirm").slideDown()
- }.bind(this))
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("settings/views/keyboard", ["require", "templates", "registry", "shared/form_model", "shared/form_validation", "global/views/keyboard", "global/collections/keyboards"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/form_model"),
- e = a("shared/form_validation"),
- f = a("global/views/keyboard"),
- g = a("global/collections/keyboards"),
- h = d.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- url: "/apiv1/student/user/account",
- defaults: {
- keyboard_id: 1
- },
- validationRules: {
- keyboard_id: {
- required: !0
- }
- }
- });
- return Backbone.View.extend({
- events: {
- "change select[name=keyboard_id]": "changeKeyboard",
- "keypress input": "submitOnEnter"
- },
- initialize: function() {
- this.template = b("settings", "keyboard"), this.keyboards = new g(FTWKEYBOARDS), this.keyboard = new f({
- type: "large",
- noHands: !0,
- noControls: !0,
- model: this.keyboards.get(c.get("user").get("keyboard_id"))
- }), this.render(), this.model = new h, this.form = this.$("form");
- var a = this.$("form .submit");
- new e(this.form, a, this.model, this.successCallback, this, !0)
- },
- render: function() {
- return this.$el.html(this.template({
- user: c.get("user").toJSON(),
- keyboards: this.keyboards.toJSON()
- })), this.$("#keyboardContainer").append(this.keyboard.render().el), this
- },
- changeKeyboard: function(a) {
- var b = $(a.target).val();
- this.keyboard.setKeyboard({
- type: "large",
- noHands: !0,
- noControls: !0,
- model: this.keyboards.get(parseInt(b, 10))
- }).render()
- },
- successCallback: function(a) {
- c.get("user").set(a), this.form.velocity("slideUp", function() {
- this.$(".confirm").slideDown()
- }.bind(this))
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("settings/views/join", ["require", "templates", "registry", "shared/form_model", "shared/form_validation"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/form_model"),
- e = a("shared/form_validation"),
- f = d.extend({
- url: "/apiv1/student/user/join",
- defaults: {
- join_code: "",
- section_id: ""
- },
- validationRules: {
- join_code: {
- required: !0
- }
- },
- validationMessages: {
- join_code: {
- INVALID: "Invalid Code. Please make sure you typed it correctly."
- }
- }
- });
- return Backbone.View.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- initialize: function() {
- this.template = b("settings", "join"), this.model = new f, this.render()
- },
- render: function() {
- this.$el.html(this.template({
- user: c.get("user").toJSON(),
- joinCode: this.model.get("join_code"),
- sections: this.sections ? this.sections.toJSON() : []
- })), this.form = this.$("form");
- var a = this.$("form .submit");
- return new e(this.form, a, this.model, this.successCallback, this, !0), this
- },
- successCallback: function(a) {
- if (a.teacher_id) c.get("user").set(a), this.form.velocity("slideUp", function() {
- this.$(".confirm").slideDown()
- }.bind(this));
- else {
- var b = this.model.toJSON();
- this.model.clear(), this.model.set({
- join_code: b.join_code,
- section_id: b.section_id
- }), this.sections = new Backbone.Collection(a), this.form.velocity("fadeOut", function() {
- this.render()
- }.bind(this), {
- visibility: "hidden"
- })
- }
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("settings/index", ["require", "templates", "registry", "settings/views/info", "settings/views/account", "settings/views/password", "settings/views/keyboard", "settings/views/join"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("settings/views/info"),
- e = a("settings/views/account"),
- f = a("settings/views/password"),
- g = a("settings/views/keyboard"),
- h = a("settings/views/join");
- return Backbone.View.extend({
- el: "#js-content",
- initialize: function() {
- return this.template = b("settings", "index"), this.user = c.get("user"), loggedIn() ? void this.render() : void(location.href = "/student")
- },
- render: function() {
- this.$el.html(this.template());
- var a = this.$(".page-settings");
- return a.append((new d).el), this.user.get("no_account_change") || "clever" == this.user.get("login_type") || a.append((new e).el), this.user.get("no_password_change") || "username" != this.user.get("login_type") || a.append((new f).el), this.user.get("no_account_change") || a.append((new g).el), "clever" != c.get("user").get("login_type") && a.append((new h).el), this
- }
- })
- }), define("upgrade/models/upgrade", ["require"], function(a) {
- "use strict";
- return Backbone.Model.extend({
- url: "/apiv1/student/upgrade"
- })
- }), define("upgrade/index", ["require", "templates", "registry", "shared/analytics", "upgrade/models/upgrade", "global/views/notice"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry"),
- d = a("shared/analytics"),
- e = a("upgrade/models/upgrade"),
- f = a("global/views/notice");
- return Backbone.View.extend({
- events: {
- "click .purchase-box .button": "upgrade"
- },
- el: "#js-content",
- initialize: function() {
- return this.template = b("upgrade", "index"), this.templateComplete = b("upgrade", "complete"), this.templateWaiting = b("upgrade", "waiting"), this.user = c.get("user"), this.model = new e, "object" != typeof StripeCheckout ? void alert('Uh oh! It looks like your network is blocking our payment gateway. To upgrade, please allow "stripe.com" in your firewall, or upgrade using a different internet connection.') : (this.stripeHandler = StripeCheckout.configure({
- key: FTWGLOBALS("stripeKey"),
- token: this.confirmOrder.bind(this),
- bitcoin: !0
- }), $(window).on("popstate", this.stripeHandler.close.bind(this.stripeHandler)), void this.render())
- },
- render: function() {
- this.$el.velocity("stop").html(this.template({
- user: this.user.toJSON(),
- price: FTWGLOBALS("product").price
- }))
- },
- renderWaiting: function() {
- this.$el.velocity("slideUp", function() {
- this.$el.html(this.templateWaiting({})).slideDown()
- }.bind(this))
- },
- renderComplete: function(a) {
- this.$el.html(this.templateComplete({
- data: a
- }))
- },
- upgrade: function() {
- return FTWGLOBALS("loggedIn") ? paid() ? void new f({
- error: !0,
- text: "Your account is already upgraded! If you are still seeing ads, try logging out and back in."
- }).show() : (this.stripeHandler.open({
- name: "Typing.com",
- description: " Account Upgrade for " + this.user.get("username"),
- amount: Math.round(100 * FTWGLOBALS("product").price),
- zipCode: !0,
- email: this.user.get("email") || null,
- allowRememberMe: !1
- }), !1) : (new f({
- error: !0,
- text: "You must log in to upgrade an account!"
- }).show(), !1)
- },
- confirmOrder: function(a) {
- this.renderWaiting(), this.model.save(a).done(function(a) {
- var b = a.data;
- b.total = b.price, b.store = "Typing.com";
- var c = [{
- product: "Premium Upgrade",
- price: b.total,
- quantity: 1,
- category: "Student"
- }];
- this.user.refreshCookie(2, !0), this.renderComplete(b), d.trackSale(b, c)
- }.bind(this)).fail(function(a) {
- this.render(), a.responseJSON ? a.responseJSON.data.declined ? new f({
- error: !0,
- text: "Your credit card was declined: " + a.responseJSON.data.declined + ". Please try again!"
- }).show() : new f({
- error: !0,
- text: "Uh oh! An error occurred: " + a.responseJSON.data.error + " Please try again!"
- }).show() : new f({
- error: !0,
- text: "An unknown error occurred! Please try again"
- }).show()
- }.bind(this))
- }
- })
- }), define("router", ["require", "index/index", "oauth/index", "index/index", "index/login", "join/index", "index/login", "password/index", "scoreboard/index", "verify/index", "lessons/index", "lessons/index", "lessons/index", "test/index", "games/play", "skins/index", "badges/index", "settings/index", "upgrade/index"], function(a) {
- "use strict";
- var b = Backbone.Router.extend({
- initialize: function() {},
- routes: {
- "": function() {
- new(a("index/index"))
- },
- oauth: function() {
- new(a("oauth/index"))
- },
- start: function() {
- new(a("index/index"))({
- start: !0
- })
- },
- login: function() {
- new(a("index/login"))
- },
- join: function() {
- new(a("join/index"))({
- code: location.hash.replace("#", "")
- })
- },
- signup: function() {
- new(a("index/login"))
- },
- password: function() {
- new(a("password/index"))
- },
- scoreboard: function() {
- new(a("scoreboard/index"))
- },
- verify: function() {
- var b;
- b = location.hash.match(/\|/) ? location.hash.replace("#", "").split("|") : location.hash.replace("#", "").split("-"), new(a("verify/index"))({
- user_test_id: parseInt(b[0]),
- user_id: parseInt(b[1])
- })
- },
- "lessons/problemkeys": function() {
- new(a("lessons/index"))({
- problemkeys: !0
- })
- },
- "lessons/:id(/:slug)(/:index)": function(b) {
- new(a("lessons/index"))({
- lessonId: b
- })
- },
- "test/:minutes": function(b) {
- new(a("lessons/index"))({
- test: !0,
- minutes: b
- })
- },
- test: function(b) {
- new(a("test/index"))
- },
- "games/play/:slug": function(b) {
- new(a("games/play"))
- },
- skins: function() {
- new(a("skins/index"))
- },
- badges: function() {
- new(a("badges/index"))
- },
- settings: function() {
- new(a("settings/index"))
- },
- upgrade: function() {
- new(a("upgrade/index"))
- }
- }
- });
- return {
- initialize: function() {
- new b, Backbone.history.start({
- pushState: !0,
- root: "/student/"
- })
- }
- }
- }), define("global/models/user", ["require", "shared/model_backends/localstorage", "registry"], function(a) {
- "use strict";
- var b = a("shared/model_backends/localstorage"),
- c = a("registry");
- return Backbone.Model.extend({
- idAttribute: "user_id",
- defaults: {
- user_id: 0,
- user_session_id: 0,
- teacher_id: null,
- section_id: null,
- username: "",
- first_name: "",
- last_name: "",
- country: "",
- email: "",
- allow_contact: 1,
- membership: "free",
- last_login: 0,
- speed: "wpm",
- spaces: 1,
- keyboard_id: 1,
- skin_id: 0,
- language: "en",
- scoreboard: 1,
- two_stars: 90,
- three_stars: 95,
- _mobileKeyboard: ""
- },
- initialize: function() {},
- refreshCookie: function(a, b) {
- a = a || 2;
- var c = $.cookie("userrem");
- if (b) {
- var d = c.split("<");
- d[2] = 2, c = d.join("<")
- }
- $.cookie("userrem", c, {
- expires: 1 / 24 * a,
- path: "/"
- })
- },
- loginWithData: function(a) {
- for (var d, e = [], f = 97; 122 >= f; f++) d = {
- id: String.fromCharCode(f),
- typed: 0,
- errors: 0
- }, a.letters_typed[String.fromCharCode(f) + "typed"] && (d.typed = a.letters_typed[String.fromCharCode(f) + "typed"]), a.letters_typed[String.fromCharCode(f) + "errors"] && (d.errors = a.letters_typed[String.fromCharCode(f) + "errors"]), e.push(d);
- var g = c.get("userLettersTyped");
- g.reset(e, {
- silent: !0
- }), g.setBackend(new b("userLettersTyped"), "model"), delete a.letters_typed;
- var h = c.get("userActivity");
- h.reset(a.activity, {
- silent: !0
- }), h.setBackend(new b("userActivity"), "model"), delete a.activity;
- var i = c.get("userLessons");
- i.reset(a.lessons, {
- silent: !0
- }), i.setBackend(new b("userLessons"), "model"), delete a.lessons;
- var j = c.get("userBadges");
- j.reset(a.badges, {
- silent: !0
- }), j.setBackend(new b("userBadges"), "model"), delete a.badges, a._lastSessionUpdate = Date.getUnixTime(), this.set(a, {
- silent: !0
- }), this.setBackend(new b("user"), "model")
- },
- clearLocalProgress: function() {
- c.get("userLettersTyped").reset(), c.get("userActivity").reset(), c.get("userLessons").reset(), c.get("userBadges").reset()
- },
- logOut: function(a) {
- a = a || {};
- var c = function() {
- $.removeCookie("userrem", {
- path: "/"
- }), $.removeCookie("teacherrem", {
- path: "/"
- }), $.removeCookie("tc_skin", {
- path: "/"
- }), b.clear()
- }.bind(this);
- return a.ignoreServer ? void c() : $.post("/apiv1/student/auth/logout").done(c)
- },
- changeSkin: function(a) {
- this.set({
- skin_id: a
- }), $.cookie("tc_skin", String(a), {
- expires: 999,
- path: "/"
- }), FTWGLOBALS("loggedIn") && $.post("/apiv1/student/user/account", {
- skin_id: a
- })
- }
- })
- }), define("global/views/header", ["require", "templates", "registry"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("registry");
- return Backbone.View.extend({
- el: "#header",
- events: {
- "click #logOutLink": "logOut"
- },
- initialize: function() {
- this.user = c.get("user"), this.template = b("global", "header"), this.render()
- },
- render: function() {
- var a = this.user.get("first_name");
- return a || "username" != this.user.get("login_type") || (a = this.user.get("username")), this.$el.html(this.template({
- data: this.user.toJSON(),
- page: FTWGLOBALS("page") || "",
- loggedIn: FTWGLOBALS("loggedIn"),
- displayName: a
- })), this
- },
- logOut: function() {
- return c.get("user").logOut().done(function() {
- location.href = "/student/start"
- }), !1
- }
- })
- }), define("global/collections/user_lessons", ["require", "registry"], function(a) {
- "use strict";
- var b = a("registry"),
- c = Backbone.Model.extend({
- idAttribute: "lesson_id",
- defaults: {
- progress: 0,
- max_progress: 0,
- typed: 0,
- errors: 0,
- seconds: 0,
- times_completed: 0,
- updated_at: 0,
- created_at: 0,
- last_save_response: null
- },
- saveStats: function(a, c) {
- return a.user_id = b.get("user").id, a.user_session_id = b.get("user").get("user_session_id"), b.get("user").get("section_id") && b.get("user").get("scoreboard") && (a.section_id = b.get("user").get("section_id")), b.get("user").get("teacher_id") && (a.teacher_id = b.get("user").get("teacher_id")), FTWGLOBALS("loggedIn") ? $.postJSON("/apiv1/student/stats", {
- stats: a,
- keys: c
- }).done(function(a) {
- a.data && this.set({
- last_save_response: a.data
- })
- }.bind(this)) : $.Deferred().resolve()
- },
- restart: function() {
- return FTWGLOBALS("loggedIn") ? $.postJSON("/apiv1/student/lessons/restart", {
- lesson_id: this.id
- }) : $.Deferred().resolve()
- }
- });
- return Backbone.Collection.extend({
- model: c
- })
- }), define("global/collections/user_activity", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "hour",
- defaults: {
- hour: 0,
- typed: 0,
- seconds: 0,
- errors: 0,
- screens: 0,
- stars: 0
- }
- });
- return Backbone.Collection.extend({
- model: b,
- comparator: "hour",
- merge: function(a) {
- a.hour = a.created_at - a.created_at % 3600, a.screens = 1, a = _.mapObject(a, parseInt);
- var b = this.get(a.hour);
- b ? b.inc({
- typed: a.typed,
- seconds: a.seconds,
- errors: a.errors,
- screens: a.screens,
- stars: a.stars
- }) : this.add(a), a.hour = 0, b = this.get(0), b ? b.inc({
- typed: a.typed,
- seconds: a.seconds,
- errors: a.errors,
- screens: a.screens,
- stars: a.stars
- }) : this.add(a)
- },
- getCompiled: function(a) {
- if (0 === a) return this.get(0) ? this.get(0).toJSON() : (new b).toJSON();
- var c = Date.getUnixTime() - 86400 * a;
- return this.toJSON().reduce(function(a, b) {
- return b.hour >= c && (a.typed += parseInt(b.typed), a.seconds += parseInt(b.seconds), a.errors += parseInt(b.errors), a.screens += parseInt(b.screens), a.stars += parseInt(b.stars)), a
- }, {
- typed: 0,
- seconds: 0,
- errors: 0,
- screens: 0,
- stars: 0
- })
- }
- })
- }), define("global/collections/user_tests", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "user_test_id",
- defaults: {
- seconds: 0,
- errors: 0,
- typed: 0,
- created_at: 0
- }
- });
- return Backbone.Collection.extend({
- model: b,
- comparator: function(a) {
- return -a.get("created_at")
- },
- url: function() {
- return "/apiv1/student/tests"
- }
- })
- }), define("global/collections/user_badges", ["require"], function(a) {
- "use strict";
- var b = Backbone.Model.extend({
- idAttribute: "lesson_id",
- defaults: {
- typed: 0,
- errors: 0,
- seconds: 0,
- created_at: 0
- }
- });
- return Backbone.Collection.extend({
- model: b,
- comparator: "created_at"
- })
- }), define("global/views/support", ["require", "templates", "shared/form_model", "shared/form_validation"], function(a) {
- "use strict";
- var b = a("templates"),
- c = a("shared/form_model"),
- d = a("shared/form_validation"),
- e = c.extend({
- url: "/apiv1/student/auth/contact",
- defaults: {
- from: "student",
- name: "",
- email: "",
- message: "",
- reason: "",
- phone: "",
- page: ""
- },
- validationRules: {
- name: {
- required: !0
- },
- email: {
- email: !0,
- required: !0
- },
- message: {
- required: !0
- },
- reason: {
- required: !0
- }
- }
- });
- return Backbone.View.extend({
- events: {
- "keypress input": "submitOnEnter"
- },
- baseModel: null,
- initialize: function(a) {
- this.options = a, this.template = b("global", "support"), this.model = new e({
- page: location.href
- })
- },
- render: function() {
- return this.$el.html(this.template({})), this.form = this.$("form"), new d(this.form, this.button, this.model, this.successCallback, this), this
- },
- ok: function() {
- this.form.submit()
- },
- successCallback: function() {
- this.trigger("complete")
- },
- submitOnEnter: function(a) {
- 13 === a.keyCode && (a.preventDefault(), $(a.currentTarget).blur(), this.form.submit())
- }
- })
- }), define("shared/helpers", ["require"], function(a) {
- "use strict";
- $(document).ajaxComplete(function(a, b, c) {
- $._lastXhr = {
- url: c.url,
- response: b.responseText
- }
- }), $.Velocity.RegisterEffect("ftw.miniShake", {
- defaultDuration: 300,
- calls: [
- [{
- translateX: -6
- }, .25],
- [{
- translateX: 6
- }, .25],
- [{
- translateX: -9
- }, .25],
- [{
- translateX: 0
- }, .25]
- ],
- reset: {
- translateX: 0
- }
- }), $.Velocity.RegisterEffect("ftw.bigShrinkIn", {
- defaultDuration: 750,
- calls: [
- [{
- opacity: [1, 1],
- scale: [1, 5]
- }]
- ]
- }), $.Velocity.RegisterEffect("ftw.perspectiveShake", {
- defaultDuration: 300,
- calls: [
- [{
- opacity: [1, 0],
- transformPerspective: [800, 800],
- rotateY: [15, -20]
- }, null, {
- easing: "easeInOutQuad"
- }],
- [{
- transformPerspective: [800, 800],
- rotateY: -15
- }, null, {
- easing: "easeInOutQuad"
- }],
- [{
- transformPerspective: [800, 800],
- rotateY: 0
- }, null, {
- easing: "easeInOutQuad"
- }]
- ],
- reset: {
- transformPerspective: 0,
- rotateY: 0
- }
- }), $.Velocity.RegisterEffect("ftw.growUp", {
- defaultDuration: 200,
- calls: [
- [{
- opacity: [1, 0],
- scale: [1, 0],
- transformOriginY: ["100%", "100%"]
- }, 1]
- ]
- }), $.Velocity.RegisterEffect("ftw.fallIn", {
- defaultDuration: 800,
- calls: [
- [{
- opacity: [1, 0],
- translateY: [0, -75],
- translateZ: 0
- }, 1, {
- easing: "spring"
- }]
- ]
- }), String.prototype.format || (String.prototype.format = function() {
- var a = arguments;
- return this.replace(/{(\d+)}/g, function(b, c) {
- return "undefined" != typeof a[c] ? a[c] : b
- })
- }), $.fn.fastHide = function() {
- return this.each(function(a, b) {
- b.style.display = "none"
- }), this
- }, $.fn.fastShow = function() {
- return this.each(function(a, b) {
- b.style.display = "block"
- }), this
- }, $.postJSON = function(a, b) {
- return $.ajax({
- type: "POST",
- url: a,
- dataType: "json",
- contentType: "application/json; charset=utf-8",
- data: JSON.stringify(b)
- })
- }, String.prototype.ucFirst = function() {
- return this.substr(0, 1).toUpperCase() + this.substr(1)
- }, $.validator.addMethod("username", function(a, b) {
- return a.toLowerCase().trim().match(/^[a-z0-9\-\._@]+$/i)
- }, "Usernames can only contain letters, numbers, underscores, dashes, and dots."), $.validator.ignoreEmailExtended = function(a, b) {
- var c = $(a).closest("form"),
- d = c.find("input[name=" + b + "]");
- return d.data("emailExtended", d.val()), c.validate().element("input[name=" + b + "]"), !1
- }, $.validator.addMethod("emailExtended", function(a, b) {
- var c = Mailcheck.run({
- email: a
- });
- return c && $(b).data("emailExtended") != a ? ($.validator.messages.emailExtended = 'This email might contain a typo or mistake. <a href="#" onclick="$.validator.ignoreEmailExtended(this, \'' + b.name + "'); return false;\">No, this is correct.</a>", !1) : !0
- }, "This email looks possibly invalid. Please double check."), $.validator.addMethod("notMatchUsername", function(a) {
- return a != this.findByName("username").val()
- }, "Can not match username"), $.validator.addMethod("ips", function(a) {
- return a.trim() ? !a.trim().split("\n").filter(function(a) {
- return !a.trim().match(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/)
- }).length : !0
- }, "Contains at least one invalid IP address. IPs are in the format A.B.C.D, for example 192.168.100.50."), $.validator.defaults.errorPlacement = function(a, b) {
- b.parent().is("label") ? b.parent().after(a) : a.insertAfter(b)
- }, Number.addCommas = function(a, b) {
- void 0 !== b && (a = Number(a).toFixed(b) || 0);
- var c = a.toString().split(".");
- c[0] = c[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
- var d = c.join(".");
- return d
- }, Number.prototype.addCommas = function(a) {
- return Number.addCommas(this, a)
- }, Number.prototype.countdownSeconds = function(a) {
- a = a || !1;
- var b = Math.floor(this / 60 / 60),
- c = Math.floor((this - 60 * b * 60) / 60),
- d = Math.floor(this - 60 * b * 60 - 60 * c),
- e = "";
- return e = String(c).pad(b > 0 || a ? 2 : 1, "0", "STR_PAD_LEFT") + ":" + String(d).pad(2, "0", "STR_PAD_LEFT"), (b > 0 || a) && (e = String(b).pad(2, "0", "STR_PAD_LEFT") + ":" + e), e
- }, String.prototype.nextLesson = function() {
- var a = this.lastIndexOf("/"),
- b = parseInt(this.substr(a + 1));
- return !this.match(/\/student\/lessons\/\d+\/\w+/) || 1 != b && 2 != b ? this : this.substr(0, a + 1) + (1 == b ? 2 : 1)
- }, String.prototype.slug = function() {
- return this.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
- }, String.prototype.pad = function(a, b, c) {
- var d, e = this,
- f = "",
- g = function(a, b) {
- for (var c = ""; c.length < b;) c += a;
- return c = c.substr(0, b)
- };
- return e += "", b = void 0 !== b ? b : " ", "STR_PAD_LEFT" !== c && "STR_PAD_RIGHT" !== c && "STR_PAD_BOTH" !== c && (c = "STR_PAD_RIGHT"), (d = a - e.length) > 0 && ("STR_PAD_LEFT" === c ? e = g(b, d) + e : "STR_PAD_RIGHT" === c ? e += g(b, d) : "STR_PAD_BOTH" === c && (f = g(b, Math.ceil(d / 2)), e = f + e + f, e = e.substr(0, a))), e
- }, Number.prototype.ordinal = function() {
- var a = ["th", "st", "nd", "rd"],
- b = this % 100;
- return a[(b - 20) % 10] || a[b] || a[0]
- }, Date.getUnixTime = function() {
- return Math.floor(Date.now() / 1e3)
- }, Number.prototype.pluralize = function(a) {
- return a = a || !1, a ? 1 == this ? "" : "es" : 1 == this ? "" : "s"
- }, Math.long2ip = function(a) {
- for (var b = a % 256, c = 1; 3 >= c; c++) a = Math.floor(a / 256), b = a % 256 + "." + b;
- return b
- }, $.getQueryParam = function(a) {
- a = a.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
- var b = new RegExp("[\\?&]" + a + "=([^&#]*)"),
- c = b.exec(location.search);
- return null === c ? "" : decodeURIComponent(c[1].replace(/\+/g, " "))
- };
- var b = function(a, b) {
- var c, d, e, f = new String,
- g = b.length;
- for (c = 0; c < a.length; c++) e = a.charAt(c), d = b.indexOf(e), d >= 0 && (e = b.charAt((d + g / 2) % g)), f += e;
- return f
- };
- window.rot47 = function(a) {
- var c = new String;
- return c = b(a, "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
- }, location.softReload = function() {
- try {
- location.href = location.href
- } catch (a) {
- location.reload(!1)
- }
- };
- var c = {};
- c.PADCHAR = "=", c.ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", c.makeDOMException = function() {
- try {
- return new DOMException(DOMException.INVALID_CHARACTER_ERR)
- } catch (a) {
- var b = new Error("DOM Exception 5");
- return b.code = b.number = 5, b.name = b.description = "INVALID_CHARACTER_ERR", b.toString = function() {
- return "Error: " + b.name + ": " + b.message
- }, b
- }
- }, c.getbyte64 = function(a, b) {
- var d = c.ALPHA.indexOf(a.charAt(b));
- if (-1 === d) throw c.makeDOMException();
- return d
- }, c.decode = function(a) {
- a = "" + a;
- var b, d, e, f = c.getbyte64,
- g = a.length;
- if (0 === g) return a;
- if (g % 4 !== 0) throw c.makeDOMException();
- b = 0, a.charAt(g - 1) === c.PADCHAR && (b = 1, a.charAt(g - 2) === c.PADCHAR && (b = 2), g -= 4);
- var h = [];
- for (d = 0; g > d; d += 4) e = f(a, d) << 18 | f(a, d + 1) << 12 | f(a, d + 2) << 6 | f(a, d + 3), h.push(String.fromCharCode(e >> 16, e >> 8 & 255, 255 & e));
- switch (b) {
- case 1:
- e = f(a, d) << 18 | f(a, d + 1) << 12 | f(a, d + 2) << 6, h.push(String.fromCharCode(e >> 16, e >> 8 & 255));
- break;
- case 2:
- e = f(a, d) << 18 | f(a, d + 1) << 12, h.push(String.fromCharCode(e >> 16))
- }
- return h.join("")
- }, c.getbyte = function(a, b) {
- var d = a.charCodeAt(b);
- if (d > 255) throw c.makeDOMException();
- return d
- }, c.encode = function(a) {
- if (1 !== arguments.length) throw new SyntaxError("Not enough arguments");
- var b, d, e = c.PADCHAR,
- f = c.ALPHA,
- g = c.getbyte,
- h = [];
- a = "" + a;
- var i = a.length - a.length % 3;
- if (0 === a.length) return a;
- for (b = 0; i > b; b += 3) d = g(a, b) << 16 | g(a, b + 1) << 8 | g(a, b + 2), h.push(f.charAt(d >> 18)), h.push(f.charAt(d >> 12 & 63)), h.push(f.charAt(d >> 6 & 63)), h.push(f.charAt(63 & d));
- switch (a.length - i) {
- case 1:
- d = g(a, b) << 16, h.push(f.charAt(d >> 18) + f.charAt(d >> 12 & 63) + e + e);
- break;
- case 2:
- d = g(a, b) << 16 | g(a, b + 1) << 8, h.push(f.charAt(d >> 18) + f.charAt(d >> 12 & 63) + f.charAt(d >> 6 & 63) + e)
- }
- return h.join("")
- }, window.btoa || (window.btoa = c.encode), window.atob || (window.atob = c.decode)
- }), define("shared/model", ["require"], function(a) {
- "use strict";
- Backbone.Model.prototype.parse = function(a) {
- return _.isObject(a.data) && _.isBoolean(a.success) ? a.data : a
- }, Backbone.Model.prototype.setBackend = function(a, b) {
- return this.backend && this.off("change", this.backend.change), a.setModel(this), this.on("change", a.change, a), "backend" == b ? this.set(a.getData()) : "model" == b && a.setData(this.toJSON()), this.backend = a, this
- }, Backbone.Model.prototype.inc = function() {
- if ("object" == typeof arguments[0]) {
- var a = null,
- b = {};
- _.each(arguments[0], function(c, d) {
- a = this.get(d) || 0, b[d] = a + c
- }, this), this.set(b, arguments[1])
- } else if ("string" == typeof arguments[0] && arguments[1]) {
- var a = this.get(arguments[0]) || 0,
- b = {};
- return b[arguments[0]] = a + arguments[1], this.set(b, arguments[3])
- }
- }
- }), define("shared/collection", ["require"], function(a) {
- "use strict";
- Backbone.Collection.prototype.parse = function(a) {
- return _.isObject(a.data) && _.isBoolean(a.success) ? a.data : a
- }, Backbone.Collection.prototype.setBackend = function(a, b) {
- return a.setModel(this), this.on("change", a.change, a), this.on("add", a.add, a), this.on("remove", a.remove, a), this.on("reset", a.reset, a), this.on("sort", a.sort, a), "backend" == b ? this.reset(a.getData()) : "model" == b && a.setData(this.toJSON()), this
- }, Backbone.Collection.prototype.sortField = "", Backbone.Collection.prototype.sortDir = "asc";
- var b = function(a, b) {
- var c = a.get(this.sortField),
- d = b.get(this.sortField),
- e = "asc" == this.sortDir ? 1 : -1;
- return d > c ? -1 * e : c > d ? 1 * e : 0
- },
- c = function(a, c) {
- var d = String(a.get(this.sortField)),
- e = String(c.get(this.sortField)),
- f = "asc" == this.sortDir ? 1 : -1;
- if ("string" != typeof d && "string" != typeof e) return b.call(this, a, c);
- var g, h, i, j, k, l, m = 0,
- n = /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g;
- if (d === e) return 0;
- for (g = d.toLowerCase().match(n) || "", h = e.toLowerCase().match(n) || "", l = g.length; l > m;) {
- if (!h[m]) return 1 * f;
- if (i = g[m], j = h[m++], i !== j) return k = i - j, isNaN(k) ? (i > j ? 1 : -1) * f : k * f
- }
- return (h[m] ? -1 : 0) * f
- };
- Backbone.Collection.prototype.reSort = function(a, b) {
- this.sortDir = b || ("asc" == this.sortDir && this.sortField == a ? "desc" : "asc"), this.sortField = a, this.comparator !== c && (this.comparator = c), this.sort()
- }
- }), define("app", ["require", "router", "registry", "shared/analytics", "shared/scoring", "shared/radar_tracker", "global/models/user", "global/views/header", "global/collections/user_lessons", "global/collections/letters_typed", "global/collections/user_activity", "global/collections/user_tests", "global/collections/user_badges", "shared/model_backends/localstorage", "shared/tooltip", "global/views/notice", "global/views/alert", "global/views/modal", "global/views/support", "shared/helpers", "shared/model", "shared/collection"], function(a) {
- "use strict";
- var b = a("router"),
- c = a("registry"),
- d = a("shared/analytics"),
- e = a("shared/scoring"),
- f = a("shared/radar_tracker"),
- g = a("global/models/user"),
- h = a("global/views/header"),
- i = a("global/collections/user_lessons"),
- j = a("global/collections/letters_typed"),
- k = a("global/collections/user_activity"),
- l = a("global/collections/user_tests"),
- m = a("global/collections/user_badges"),
- n = a("shared/model_backends/localstorage"),
- o = a("shared/tooltip"),
- p = a("global/views/notice"),
- q = a("global/views/alert"),
- r = a("global/views/modal"),
- s = a("global/views/support");
- return a("shared/helpers"), a("shared/model"), a("shared/collection"), {
- initialize: function() {
- this.browserCheck() && (window.__NO_SCRIPTS || (this.globals(), "production" == FTWGLOBALS("env") && this.analytics(), this.authentication({
- cookieName: "userrem"
- }), this.views(), this.support(), this.pageTrack(), this.router(), this.tracker(), this.tooltips()))
- },
- browserCheck: function() {
- var a = navigator.userAgent;
- if (a.match(/MSIE/) && parseInt(a.match(/MSIE (\d+)/)[1], 10) < 10) return location.href = "/failbrowser", !1;
- if (a.match(/Firefox/) && parseInt(a.match(/Firefox\/(\d+)/)[1], 10) < 29) return location.href = "/failbrowser", !1;
- if (!a.match("Chrome") && a.match("Safari") && a.match(/Version\/(\d+.\d+)/)) {
- var b = parseFloat(a.match(/Version\/(\d+.\d+)/)[1]);
- if (5.1 > b) return location.href = "/failbrowser", !1
- }
- if (!window.__NO_SCRIPTS) {
- try {
- window.localStorage.setItem("test", "test")
- } catch (c) {
- return navigator.userAgent.match(/safari/i) ? alert("Typing.com can not be used in Safari while in Private Browsing Mode due to a bug in Safari. Please switch to normal browsing mode to use Typing.com, or switch to a different browser.") : alert("Error: Your browser does not support Local Storage, or your Local Storage is full. You will not be able to use Typing.com until this is resolved. Try updating your browser, or if you are in Private Browsing mode try normal mode."), !1
- }
- return !0
- }
- },
- globals: function() {
- c.set("data", window.FTW), c.set("isMobile", navigator.userAgent.match(/iPad|iPhone|Android/));
- var a = _.extend({}, Backbone.Events);
- c.set("pubSub", a);
- var b = new g;
- b.setBackend(new n("user"), "backend"), c.set("user", b);
- var d = new i;
- d.setBackend(new n("userLessons"), "backend"), c.set("userLessons", d);
- var e = new k;
- e.setBackend(new n("userActivity"), "backend"), c.set("userActivity", e);
- var f = new m;
- f.setBackend(new n("userBadges"), "backend"), c.set("userBadges", f);
- var h = new l;
- h.setBackend(new n("userTests"), "backend"), c.set("userTests", h);
- var o = new j;
- o.setBackend(new n("userLettersTyped"), "backend"), c.set("userLettersTyped", o)
- },
- authentication: function(a) {
- $.ajaxSetup({
- headers: {
- "X-CSRFToken": c.get("user").get("token"),
- "X-User-Id": c.get("user").id
- }
- });
- var b = this;
- if ($(document).ajaxError(function(a, d) {
- if (d.responseJSON && d.responseJSON.data) {
- var e = d.responseJSON.data.error;
- !FTWGLOBALS("loggedIn") || "LOGIN_REQUIRED" != e && "TOKEN_MISMATCH" != e || (console.error("Invalid token or session"), c.get("user").logOut({
- ignoreServer: !0
- }), "production" == FTWGLOBALS("env") ? location.href = "/student" : b.devLoggedOutNotice("Bad AJAX response: " + JSON.stringify({
- error: e,
- response: d
- })))
- }
- }), location.pathname.match(/^\/student\/oauth/)) return !0;
- var d = FTWGLOBALS("loggedIn"),
- e = c.get("user"),
- f = $.cookie(a.cookieName) || "",
- g = f.split("<"),
- h = g[1];
- if (d) {
- if (!e.id || h != e.id) return e.logOut().done(function() {
- "production" == FTWGLOBALS("env") ? location.reload() : b.devLoggedOutNotice("LocalStorage ID does not match cookieUserId: " + JSON.stringify({
- "user.id": e.id,
- cookieUserId: h
- }))
- }), !1;
- var i = 2;
- e.refreshCookie(i), setTimeout(this.loggedOutNotice.bind(this), 3600 * i * 1e3)
- } else e.id && (e.logOut({
- ignoreServer: !0
- }), "local" == FTWGLOBALS("env") && b.devLoggedOutNotice("Not logged in, but has local storage: " + JSON.stringify({
- cookieParts: g,
- cookieUserId: h
- })), this.globals());
- return !0
- },
- loggedOutNotice: function() {
- new q({
- title: "Logged Out",
- text: "This account has been logged out due to inactivity. You must log back in to continue"
- }).show().on("close", function() {
- location.href = "/student/login"
- })
- },
- views: function() {
- new h({
- model: c.get("user")
- })
- },
- support: function() {
- FTWGLOBALS("loggedIn") && c.get("user").get("teacher_id") ? $("#supportTab").remove() : $("#supportTab").fastShow().click(function() {
- var a = new r({
- title: "Contact Support",
- okButton: "Send",
- cancelButton: "Cancel",
- contentView: new s,
- position: "absolute"
- });
- return a.on("complete", function() {
- new p({
- error: !1,
- text: "Your support request has been sent. We will get back to you shortly!"
- }).show()
- }.bind(this)), a.show(), !1
- })
- },
- devLoggedOutNotice: function(a) {
- console.trace(), new q({
- title: "DEBUGGING NOTICE: Logged Out",
- text: "This account was just logged out. Look at the console. " + a
- }).show()
- },
- router: function() {
- b.initialize()
- },
- tracker: function() {
- location.href.match(/lessons\/[0-9]+/) || f.track(c.get("user"))
- },
- pageTrack: function() {
- var a = rot47("student_pages"),
- b = localStorage.getItem(a) || 0;
- b++, localStorage.setItem(a, b)
- },
- analytics: function() {
- var a = c.get("user");
- if (d.customDimension(1, FTWGLOBALS("loggedIn") ? "yes" : "no"), FTWGLOBALS("loggedIn")) {
- a.get("teacher_id") ? d.customDimension(2, "in class") : d.customDimension(2, "individual"), d.customDimension(3, a.get("login_type")), d.customDimension(4, a.get("membership")), d.customDimension(5, Math.floor(Math.floor(Date.now() / 1e3 - a.get("created_at")) / 60 / 60 / 24));
- var b = c.get("userActivity").getCompiled(999),
- f = e.speed(b.typed, b.seconds);
- f > 0 && (f = 10 * Math.floor(f / 10), d.customDimension(6, f + "-" + (f + 9)));
- var g = c.get("userLessons").toJSON().filter(function(a) {
- return a.progress > 0
- }).length;
- d.customDimension(7, g)
- } else d.customDimension(2, "anon");
- ga("send", "pageview")
- },
- tooltips: function() {
- o.init()
- }
- }
- }), require.config({
- urlArgs: "bust=" + (new Date).getTime(),
- baseUrl: "/src/student/js",
- paths: {
- shared: "../../shared",
- analytics: "../../shared/analytics",
- registry: "../../shared/registry",
- templates: "../../shared/templates"
- }
- }), require(["app"], function(a) {
- $(function() {
- a.initialize()
- })
- }), define("main", function() {}), require(["app"]);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement