UNPKG

jive-sdk

Version:

Node.js SDK for Jive Software to assist with the development of add-ons

605 lines (491 loc) 20.8 kB
window.GalleryEditView = Backbone.View.extend({ didNotChangeImages: true, events:{ "click #uploadbtn" : "triggerUpload", "click #link_save" : "saveGallery", "click #link_cancel" : "cancelEdit", "click #gallery_title" : "selectText", "change #upload" : "uploadFiles", "drop .j-draganddrop" : "uploadFiles", "click .green.addmore": "triggerUpload" }, initialize:function (options) { this.render(); this.imageProcessor = new ImageProcessor(); this.preventDefaultDropBehavior(); }, render:function () { var dom = $(this.el); var resultDom = dom.find("#result"); dom.html(_.template(this.options.container.getTemplate('#GalleryEdit'))); this.addDropEvents(); }, deactivateIE: function() { var dom = $(this.el); var ieUploadButton = dom.find("#ieUpload"); ieUploadButton.hide(); }, setupIE: function() { var dom = $(this.el); var ieUploadButton = dom.find("#ieUpload"); dom.find("#upload-files").hide(); var base = gadgets.util.getUrlParameters()["url"].replace("/app.xml", ""); var imageTo64 = gadgets.io.getProxyUrl(base + "/flashupload/bin/flashupload.swf?cachebust=" + Math.random()); var expressInstall = gadgets.io.getProxyUrl(base + "/flashupload/expressInstall.swf"); var version = "10.0.0"; $.fn.addFlashUpload = function() { return this.each(function() { var $this = $(this); var uploadId = "flashUpload-" + $.fn.addFlashUpload.id++; var width = $this.outerWidth(); var height = $this.outerHeight(); var position = $this.css("position"); // this span will get replaced by the Flash content $this.attr("data-uploadid", uploadId).append('<span id="' + uploadId + '"></span>'); // be sure the parent (button) has relative/absolute positioning so that our Flash can sit on top if (position !== "absolute" && position !== "relative") { $this.css("position", "relative"); } // pass in the uploadId to the Flash for it to pass back out on upload var flashvars = { uploadId: uploadId }; // make transparent and able to access JavaScript var params = { wmode: "transparent", allowscriptaccess: "always" }; // position over the top of the button the Flash is inside of var attributes = { style: "position: absolute; z-index: 100; left: 0; right: 0; top: 0; bottom: 0;" }; // embed the Flash swfobject.embedSWF(imageTo64, uploadId, width, height, version, expressInstall, flashvars, params, attributes); }); }; $.fn.addFlashUpload.id = 1; // handle the uploads and use jQuery's events to notify listeners of this button's upload window.processBase64Encodings = function( fileName, fileData, uploadId ) { $( "[data-uploadid=" + uploadId + "]" ).trigger( "upload", [ fileName, fileData ] ); }; // Handle IE upload button var self = this; ieUploadButton.addFlashUpload().bind( "upload", function( event, fileName, fileData ) { var mime = fileName.split(".").pop(); mime = mime.toLowerCase(); if (mime === "jpg") { mime = "jpeg"; } var container = self.options.container; var successCallback = function(imgData ) { var galleryEntryView = new GalleryEntryView({ container: container }); var imgDom = galleryEntryView.render(imgData); // Hide IE Upload Button var dom = $(self.el); ieUploadButton.hide(); imgDom.prependTo("#result") .hide() .css({ 'z-index':self.zindex++ }) .show(); self.setupDropEvents(); $(".imagesAdded-addmore").show(); // show add more in the right column $("#link_save").show(); $("#link_save").prop("disabled", false); }; self.imageProcessor.buildImage( fileName, "data:image/" + mime + ";base64," + fileData, successCallback ); }); }, renderGallery: function( gallery ) { var dom = $(this.el); var title = gallery.get("title"); title = this.getSafeTitle(title); dom.find("#gallery_title").val(title); dom.find('#gallery_title').select(); // load images var ids = gallery.get("imgIDs"); if(ids.length) { $("#dnd-blurb", dom).hide(); // hide upload button from d-n-d area $(".imagesAdded-addmore", dom).show(); $("#link_save").prop("disabled", false); } else { $("#dnd-blurb", dom).show(); // show upload button from d-n-d area $(".imagesAdded-addmore", dom).hide(); } if ( !ids || ids.length < 1 ) { // nothing else to do if there are no images return; } console.log("Prepping image id manifest", ids); var galleryEntryMap = {}; var container = this.options.container; // pre-seed the gallery entry doms $.each( ids, function(index) { if(index == 30) { osapi.jive.core.container.sendNotification({message: 'You can have only 30 images per album.', severity: 'error'}); } if(index > 29) { return; } var imageId = this; var model = new GalleryEntry({id: imageId}); var view = new GalleryEntryView({model: model, container: container }); galleryEntryMap[imageId] = { view: view, model: model }; var entryDom = view.render(); dom.find("#result").append( entryDom ); }); // now load $.each( ids, function(index) { if(index > 29) { return; } var imageId = this; console.log("loading image id " + imageId ); var galleryEntry = galleryEntryMap[imageId]; var model = galleryEntry.model; var view = galleryEntry.view; model.bind("fetchedThumbnail", function(thumbImage) { view.renderThumbnail(thumbImage); }); model.bind("fetchedFullSource", function(fullImage) { view.attachFullImageSource(fullImage); }); model.fetch(); }); this.setupDropEvents(); }, setupDropEvents: function() { var that = this; $(this.el).find("#result").sortable( { tolerance: "pointer", opacity: 0.8 }).disableSelection(); // $(this.el).find("#trashcan_img").droppable({ // accept: "#result img", // tolerance: "touch", // drop: function(event, ui) { // //remove image from the DOM. Upon saving it will be removed // ui.draggable.remove(); // $(that.el).find("#trashcan_img_active").hide(); // $(that.el).find("#trashcan_img").show(); // }, // activate: function(event, ui) { // $(that.el).find("#trashcan_img").hide(); // $(that.el).find("#trashcan_img_active").show(); // }, // deactivate: function(event, ui) { // $(that.el).find("#trashcan_img_active").hide(); // $(that.el).find("#trashcan_img").show(); // } // // }); }, triggerUpload: function(event) { $("#upload").click(); event.preventDefault(); }, saveGallery: function() { var self = this; var dom = $(this.el); var gallery = this.model; var images = dom.find("#result img"); if ( images.length < 1 ) { console.log("Nothing to save"); osapi.jive.core.container.sendNotification({message: 'Please upload at least one image to save this album.', severity: 'info'}); return; } var galleryList = this.options.listModel; // todo if (galleryList.titleExists(gallery)) ... show warning about duplicate title $('#link_save').prop('disabled', true); //First pass, generate new IDs, set -1 if it didn't have one already (new image) var imageIDs = []; $.each( images, function() { var image = $(this); var imageID = image.attr("data-id") || -1; //-1 means we need to gen a new ID imageIDs.push(imageID); }); //Second pass--generate random IDs, checking that they don't exist already. for (var i = 0; i < imageIDs.length; i++) { if (imageIDs[i] === -1) { do { var newId = randomNum(); } while (imageIDs.indexOf(newId) >= 0); imageIDs[i] = newId; } } // save gallery images first var container = this.options.container; this.saveGalleryImages( images, imageIDs, function() { var title = dom.find("#gallery_title").val(); title = self.getSafeTitle(title); var previewView = new GalleryPreviewView({ container: container }); var onloadPromises = $.map( images, function(img) { var i = $(img); var t = $("<img>").attr("src", i.attr("data-fullsrc")); $("#secret").append(t); var deferred = $.Deferred(), timeout = setTimeout(deferred.resolve, 1500); t.on('load', function() { clearTimeout(timeout); deferred.resolve(); }); return deferred.promise(); }); $.when.apply($, onloadPromises).done(function() { previewView.generatePreview($("#secret"), title, images.length).done(function(previewSrc) { gallery.set( { "title": title, "imgIDs": imageIDs, "preview": previewSrc } ); // tell the gallery model to save itself gallery.save(); }); }); }); }, saveGalleryImages: function(images, imageIDs, successCallback) { var savedCount = 0; var destroyCount = 0; var gallery = this.model; var oldIDs = gallery.get('imgIDs') == "" ? [] : gallery.get('imgIDs'); //figure out difference between oldIDs and imageIDs in DOM (previous gallery state vs. new state) var toDelete = []; var toUpload = []; var that = this; //Go through the new IDs, and mark for upload if they're not in the old IDs $.each(imageIDs, function(index, imgId) { if (oldIDs.indexOf(imgId) < 0) { toUpload.push( { id: imgId, dom: $(that.el).find("#result").find("[data-id=" + imgId + "]")[0] }); //place new ID (may be generated) and img element in array } }); //Go through the old IDs, and mark for deletion if they're not in the new IDs $.each(oldIDs, function(index, imgId) { if (imageIDs.indexOf(imgId) < 0 ) { toDelete.push({ id: imgId }); //for deleting, just push ID } }); //Callback so that images are deleted sequentially after saving. var deleteImagesAfterSaving = function() { if (toDelete.length == 0) { successCallback(); } $.each( toDelete, function(index, value) { destroyCount++; var data = { imageId : value.id }; var callback = function( theID ) { console.log("Destroyed image id " + theID + " source"); }; var galleryEntryModel = new GalleryEntry(); galleryEntryModel.destroy( data, callback ); if ( destroyCount == toDelete.length ) { console.log("Done destroying " + destroyCount); successCallback(); } }) }; if (toUpload.length == 0) { //Don't make a save request if there are no images to upload deleteImagesAfterSaving(); } else { var count = 0; $.each( toUpload, function(index, value) { var image = $(value.dom); var data = { previewSrc: image.attr("src"), fullSrc : image.attr("data-fullsrc"), width : image.attr("data-width"), height : image.attr("data-height"), imageId : value.id }; var callback = function( imageId ) { console.log("Saved image id " + imageId + " source"); savedCount++; if ( savedCount == toUpload.length) { console.log("Done saving " + savedCount); deleteImagesAfterSaving(); } }; window.setTimeout( function() { var galleryEntryModel = new GalleryEntry(); galleryEntryModel.save( data, callback ); //Callback = delete imgs in toDelete }, count * 0 ); // change 0 to some value to throttle requests }); } }, cancelEdit: function() { Backbone.history.navigate('gallery-edit-cancel', true); }, uploadFiles: function(event) { var self = this; if (event.originalEvent && event.originalEvent.dataTransfer && event.originalEvent.dataTransfer.files) { event.preventDefault(); var files = event.originalEvent.dataTransfer.files; } else { var files = event.target.files; } this.didNotChangeImages = false; var startedCallback = function(file) { $("#link_save").prop('disabled', true); $("#dnd-blurb").hide(); // hide upload button from d-n-d area self.showSpinner(); }; var html = "<div class=\"ui-progress-bar\" id=\"progress_bar\" style=\"\">" + "<div class=\"ui-progress\" style=\"width: 50%;\"></div>" + "</div>"; $(html).prependTo("#result"); var allDoneCallback = function() { $("#progress_bar").remove(); self.setupDropEvents(); $(".imagesAdded-addmore").show(); // show add more in the right column $("#link_save").prop('disabled', false); self.hideSpinner(); }; var successCallback = function(file, imgData) { var galleryEntryView = new GalleryEntryView({ container: self.options.container }); var imgDom = galleryEntryView.render(imgData); //Do not add files more than 30 if($("#result .result_img").length > 30) { return; } imgDom.prependTo("#result") .hide() .css({ 'z-index':self.zindex++ }) .show(); }; var errorCallback = function(file, error) { alert(error); }; var promises; if(files && typeof FileReader !== "undefined") { //process each files only if browser is supported if($("#result .result_img").length + files.length > 30) { osapi.jive.core.container.sendNotification({message: 'You can have only 30 images per album.', severity: 'error'}); return; } promises = $.map(files, function(file) { return self.imageProcessor.readFile( file, startedCallback ).then(successCallback, errorCallback); }); $.when.apply($, promises).done(allDoneCallback); } else { // hmm?? // xxx todo -- a good error! } }, selectText: function(event) { event.currentTarget.select(); $(event.currentTarget).unbind('click'); }, prettyDate: function(date) { function pad(number) { var r = String(number); if ( r.length === 1 ) { r = '0' + r; } return r; } //It makes me cry that this isn't provided by javascript function getPrettyMonth(m) { switch (m) { case 0: return "Jan."; case 1: return "Feb."; case 2: return "Mar."; case 3: return "Apr."; case 4: return "May"; case 5: return "June"; case 6: return "July"; case 7: return "Aug."; case 8: return "Sep."; case 9: return "Oct."; case 10: return "Nov."; case 11: return "Dec."; } return ""; //fail } function getPrettyTime(h, m) { return ( (h % 12 == 0) ? "12" : ( h % 12 ) ) + ":" + pad(m) + (h < 12 ? " AM" : " PM"); } return getPrettyMonth(date.getMonth()) + ' ' + date.getDate() + ', ' + date.getFullYear() + ' ' + getPrettyTime( date.getHours(), date.getMinutes()); }, showSpinner: function() { $('.j-loading-images').show(); }, hideSpinner: function() { $('.j-loading-images').hide(); }, getSafeTitle: function(value) { value = $.trim(value); value = $('<div/>').text(value).html(); // escapes html tags, etc. if (!value) { value = this.prettyDate(new Date()); } return value; }, // Prevent the page location from changing to a dropped file // when a file is dropped anywhere on the page. This should // help to prevent confusion when a user tries to drop // a file on an element that is not actually a file drop // target. preventDefaultDropBehavior: function() { $(document) .on('dragover', function(event) { var onFileInput = event.target && event.target.type === 'file'; if (!onFileInput) { event.preventDefault(); event.stopPropagation(); } }) .on('drop', function(event) { var dt = event.originalEvent.dataTransfer , files = dt && dt.files && dt.files.length > 0 , onFileInput = event.target && event.target.type === 'file'; if (files && !onFileInput) { event.preventDefault(); } }); }, addDropEvents: function() { var view = this, $elem = $(this.el), target = '.j-draganddrop'; $elem .on('dragenter', target, function(event) { // IE10 requires a preventDefault() call here to make // $target a drop target. // http://blogs.msdn.com/b/ie/archive/2011/07/27/html5-drag-and-drop-in-ie10-ppb2.aspx event.preventDefault(); }) .on('dragleave', target, function() { $(this).removeClass('j-drag-over'); }) .on('dragover', target, function(event) { // Allows dragged elements to be dropped here. Not all // browsers require stopPropagation() to be called; but // apparently some do. // http://www.html5rocks.com/en/tutorials/dnd/basics/ event.preventDefault(); event.stopPropagation(); // Setting a class on 'dragover' seems to be more // reliable than using the 'dragenter' event. $(this).addClass('j-drag-over'); event.originalEvent.dataTransfer.dropEffect = 'copy'; }); //.on('drop', target, function(event) { // event.preventDefault(); // var files = event.originalEvent.dataTransfer.files; // if (files.length >= 1) { // view.select(files); // } //}); } });