define(["jquery", "underscore", "backbone", "localforage", "clipboard", "text!templates/draftsTemplate.html"], function($, _, Backbone, LocalForage, Clipboard, draftsTemplate){ /** * @class DraftsView * @classdesc A view that lists the local submission drafts for this user * @classcategory Views */ var view = Backbone.View.extend( /** @lends DraftsView.prototype */{ type: "DraftsView", el: "#Content", className: "div", template: _.template(draftsTemplate), initialize: function() { return this; }, render: function() { var view = this; var drafts = []; LocalForage.iterate(function(value, key, iterationNumber) { // Extract each draft drafts.push({ key: key, value: value, fileName: (typeof value.title === "string") ? value.title.substr(0, 50).replace(/[^a-zA-Z0-9_]/, "_") : "draft", friendlyTimeDiff: view.friendlyTimeDiff(value.datetime) }); }).then(function(){ // Sort by datetime drafts = _.sortBy(drafts, function(draft) { return draft.value.datetime.toString(); }).reverse(); }).then(function() { // Render view.$el.html( view.template({ drafts: drafts }) ); // Insert downloadables view.insertDownloadables(); // Insert copiables view.insertCopiables(); }).catch(function(err) { console.log(err); view.$el.html("
There was an error listing drafts.
"); }); return this; }, /** Attach a click handler for download buttons that triggers a draft * or all drafts to be downloaded */ insertDownloadables: function() { var view = this; // Build handlers for single downloaders _.each(this.$el.find(".draft-download"), function(el) { var a = $(el).find("a.download"); var text = $(el).find("textarea")[0].value; var fileName = a.data("filename") || "draft.xml"; $(a).on("click", view.createDownloader(text, fileName)); }); // Build handler for Download All button this.$el.find(".download-all").on("click", this.createDownloadAll()); }, /** Creates a function for use as an event handler in insertDownloadables * that creates a closure around the content (text) and filename and * causes the browser to download the draft when clicked */ createDownloader: function(text, fileName) { return function() { var blob = new Blob([text], { type: "application/xml" }) var url = window.URL.createObjectURL(blob); var a = document.createElement("a"); a.style = "display: none;"; a.href = url; a.download = fileName; a.click(); a.remove(); } }, createDownloadAll: function() { var drafts = []; _.each(this.$el.find("textarea"), function(textarea) { drafts.push(textarea.value); }); var doc = "\n\n" + _.map(drafts, function(draft) { return "\t\n\t\t" + draft + "\n\t\n" }).join("") + ""; return function() { var blob = new Blob([doc], { type: "application/xml" }) var url = window.URL.createObjectURL(blob); var a = document.createElement("a"); a.style = "display: none;"; a.href = url; a.download = "drafts.xml"; a.click(); a.remove(); } }, insertCopiables: function() { var copiables = $(".copy-to-clipboard"); _.each(copiables, function(copiable, i) { var clipboard = new Clipboard(copiable, { text: function(trigger) { return $("#draft-" + i).text() } }); clipboard.on("success", function(e) { var el = $(e.trigger); $(el).html( $(document.createElement("span")).addClass("icon icon-ok success") ); // Use setTimeout instead of jQuery's built-in Events system because // it didn't look flexible enough to allow me update innerHTML in // a chain setTimeout(function() { $(el).html(' Copy to Clipboard'); }, 500) }); }); }, /** * Formats a time difference, in milliseconds, in a human-friendly way * @param {string} datetime: A datetime as a string which needs to be * parsed before working with */ friendlyTimeDiff: function(datetime) { var friendly, now = new Date(), then = new Date(datetime), diff = now - then; // Fall through from largest to smallest, finding the largest unit // that describes the difference with a unit value of one or greater if (diff > 2678400000) { friendly = { value: Math.round(diff / 2678400000) , unit: "month" } } else if (diff > 604800000) { friendly = { value: Math.round(diff / 604800000), unit: "week" } } else if (diff > 86400000) { friendly = { value: Math.round(diff / 86400000), unit: "day" } } else if (diff > 3600000) { friendly = { value: Math.round(diff / 3600000), unit: "hour" } } else if (diff > 60000) { friendly = { value: Math.round(diff / 60000), unit: "minute" } } else if (diff > 1000) { friendly = { value: Math.round(diff / 1000), unit: "second" } } else { // Shortcircuit if really small and return... return "just now"; } // Pluralize if (friendly.value !== 1) { friendly.unit = friendly.unit + "s" } return friendly.value + " " + friendly.unit + " ago"; } }) return view; });