PageRenderTime 42ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/webtop/js/bulkUploader/bulk-upload.js

https://bitbucket.org/farcry/core
JavaScript | 603 lines | 516 code | 74 blank | 13 comment | 60 complexity | 3b430a2bac8e95ccee739623aba8873b MD5 | raw file
  1. FileModel = Backbone.Model.extend({
  2. idAttribute : "fileID"
  3. });
  4. FileCollection = Backbone.Collection.extend({
  5. model : FileModel,
  6. initialize : function FileCollection_initialize(options){
  7. this.statusCheckTimer = 0;
  8. _.bindAll(this,"checkStatus","updateStatus");
  9. this.listenTo(this,"add",this.addFile,this);
  10. this.listenTo(this,"change:status",this.changeFile,this);
  11. },
  12. addFile : function FileCollection_addFile(file){
  13. if (file.get("status") === "uploaddone" && this.statusCheckTimer === 0) {
  14. this.statusCheckTimer = setTimeout(this.checkStatus,1000);
  15. }
  16. },
  17. changeFile : function FileCollection_changeFile(file){
  18. var processing = this.where({
  19. status : "uploaddone"
  20. });
  21. if (!this.statusCheckTimer && processing.length){
  22. this.statusCheckTimer = setTimeout(this.checkStatus,500);
  23. }
  24. else if (this.statusCheckTimer && processing.length===0){
  25. clearTimeout(this.statusCheckTimer);
  26. this.statusCheckTimer = 0;
  27. }
  28. },
  29. updateOptions : function FileCollection_updateOptions(options){
  30. this.options = this.options || {};
  31. for (var k in options)
  32. this.options[k] = options[k];
  33. },
  34. getFileIDs : function FileCollection_getFileIDs(){
  35. return this.pluck("fileID");
  36. },
  37. checkStatus : function FileCollection_checkStatus(){
  38. Backbone.$.getJSON(this.options.statusURL + (this.options.statusURL.indexOf("?")>-1 ? "&" : "?") + "&uploader=" + this.options.uploaderID, this.updateStatus);
  39. },
  40. updateStatus : function FileCollection_updateStatus(result){
  41. if (result.error) {
  42. if (result.error.message !== this.previousError) {
  43. this.options.generalErrors.add({
  44. error: result.error
  45. });
  46. }
  47. this.previousError = result.error.message;
  48. }
  49. else {
  50. if (this.previousError)
  51. delete this.previousError;
  52. // make sure all the CSS / JS required for the forms have been loaded
  53. for (var i = 0, ii = result.htmlhead.length; i < ii; i++) {
  54. if (result.htmlhead[i].id !== "onready") {
  55. if (Backbone.$("#" + result.htmlhead[i].id).size() === 0) {
  56. Backbone.$("head").append(result.htmlhead[i].html);
  57. }
  58. }
  59. }
  60. // update the queue with the edit forms
  61. for (var i = 0, ii = result.files.length; i < ii; i++) {
  62. var file = this.get(result.files[i].result.objectID);
  63. if (file && result.files[i].error) {
  64. file.set({
  65. status: "failed",
  66. error: result.files[i].error
  67. });
  68. }
  69. else {
  70. if (file) {
  71. file.set({
  72. status: "editable",
  73. teaserHTML: result.files[i].teaserHTML,
  74. editHTML: result.files[i].editHTML
  75. });
  76. }
  77. }
  78. }
  79. // run all "onready" JavaScript
  80. for (var i = 0, ii = result.htmlhead.length; i < ii; i++) {
  81. if (result.htmlhead[i].id === "onready") {
  82. eval(result.htmlhead[i].html);
  83. }
  84. }
  85. }
  86. // if there are still items pending, queue another check
  87. var processing = this.where({
  88. status : "uploaddone"
  89. });
  90. if (processing.length)
  91. this.statusCheckTimer = setTimeout(this.checkStatus,500);
  92. else
  93. this.statusCheckTimer = 0;
  94. }
  95. });
  96. FileView = Backbone.View.extend({
  97. initialize : function FileView_initialize(options){
  98. this.templates = {
  99. added : Handlebars.compile(Backbone.$("#added-file-template").html()),
  100. uploading : Handlebars.compile(Backbone.$("#uploading-file-template").html()),
  101. uploaddone : Handlebars.compile(Backbone.$("#uploaddone-file-template").html()),
  102. editable : Handlebars.compile(Backbone.$("#editable-file-template").html()),
  103. saved : Handlebars.compile(Backbone.$("#saved-file-template").html()),
  104. failed : Handlebars.compile(Backbone.$("#failed-file-template").html())
  105. };
  106. this.listenTo(this.model,"change",this.render,this);
  107. },
  108. events : {
  109. "click .remove" : "removeFile",
  110. "click .save" : "saveFile",
  111. "click .icon-info" : "showInfo"
  112. },
  113. render : function FileView_render(){
  114. if (this.model.hasChanged("status") || this.$el.html() === "")
  115. // if the status has changed, replace the HTML
  116. this.$el.html(this.templates[this.model.get("status")](this.model.attributes));
  117. else
  118. // if the status is the same, call the status specific update function
  119. this[this.model.get("status")+"Update"]()
  120. },
  121. removeFile : function FileView_removeFile(){
  122. if (this.model.has("jqXHR"))
  123. this.model.get("jqXHR").abort();
  124. // remove file from memory and DOM
  125. this.model.collection.remove(this.model);
  126. this.remove();
  127. delete this.model;
  128. },
  129. saveFile : function FileView_saveFile(){
  130. var formPrefix = this.$("input[name=FarcryFormPrefixes]").val(),
  131. formData = serializeFormByPrefix(formPrefix,this.options.collectionView.options.editableProperties,true)
  132. self = this;
  133. this.$el.block();
  134. Backbone.$.post(this.model.get("saveURL"), formData, function(result){
  135. if (result.error){
  136. self.model.set({
  137. status : "error",
  138. error : result.error
  139. });
  140. }
  141. else{
  142. self.model.set({
  143. status : "saved",
  144. teaserHTML : result.teaserHTML,
  145. editHTML : result.editHTML
  146. });
  147. }
  148. self.$el.unblock();
  149. },"json");
  150. },
  151. showInfo : function FileView_showInfo(){
  152. this.$(".info").toggle();
  153. },
  154. addedUpdate : function FileView_addedUpdate(){
  155. // no updates
  156. },
  157. uploadingUpdate : function FileView_uploadingUpdate(){
  158. var progress = this.model.get("progress");
  159. this.$(".bar").stop().animate({
  160. "width": formatPercentage(progress.loaded, progress.total)
  161. }, "fast");
  162. this.$(".progress-loaded").text(formatFileSize(progress.loaded));
  163. this.$(".progress-total").text(formatFileSize(progress.total));
  164. this.$(".progress-bitrate").text(formatBitRate(progress.bitrate));
  165. },
  166. uploaddoneUpdate : function FileView_uploaddoneUpdate(){
  167. // no updates
  168. },
  169. editableUpdate : function FileView_editableUpdate(){
  170. this.$(".teaser").html(this.model.get("teaserHTML"));
  171. },
  172. savedUpdate : function FileView_savedUpdated(){
  173. // no updates
  174. },
  175. failedUpdate : function FileView_failedUpdate(){
  176. // noupdates
  177. }
  178. });
  179. FileCollectionView = Backbone.View.extend({
  180. initialize : function FileCollectionView_initialize(options){
  181. this.listenTo(this.collection,"add",this.addFile,this);
  182. },
  183. addFile : function FileCollectionView_addFile(file){
  184. var fileView = new FileView({
  185. model : file,
  186. collectionView : this
  187. });
  188. this.$el.append(fileView.el);
  189. fileView.render();
  190. }
  191. });
  192. FileUploadView = Backbone.View.extend({
  193. initialize : function FileUploadView_initialize(options){
  194. this.template = Handlebars.compile(Backbone.$("#upload-area-template").html());
  195. if (options.uploadURL === undefined)
  196. throw "uploadURL must be defined in FileUploadView options";
  197. if (options.saveURL === undefined)
  198. throw "saveURL must be defined in FileUploadView options";
  199. if (options.uploaderID === undefined)
  200. throw "uploaderID must be defined in FileUploadView options";
  201. this.fileUpload = {
  202. url : options.uploadURL,
  203. dataType : "json",
  204. sequentialUploads : true,
  205. limitMultiFileUploads : 1,
  206. autoUpload : true
  207. };
  208. if (options.sizeLimit)
  209. this.fileUpload.maxFileSize = options.sizeLimit;
  210. if (options.allowedExtensions)
  211. this.fileUpload.acceptFileTypes = new RegExp("\.("+options.allowedExtensions.replace(/,/g,"|")+")$","i");
  212. Backbone.$(document).bind('drop dragover', function (e) {
  213. e.preventDefault();
  214. });
  215. this.stopShow = 0;
  216. this.render();
  217. },
  218. events : {
  219. "fileuploadadd #fileupload" : "uploadAdd",
  220. "fileuploadsubmit #fileupload" : "uploadSubmit",
  221. "fileuploadsend #fileupload" : "uploadSend",
  222. "fileuploadprogress #fileupload" : "uploadProgress",
  223. "fileuploaddone #fileupload" : "uploadDone",
  224. "fileuploadfail #fileupload" : "uploadFail",
  225. "dragenter .targetarea" : "dragEnter",
  226. "dragover .targetarea" : "dragOver",
  227. "dragleave .targetarea" : "dragLeave"
  228. },
  229. render : function FileUploadView_render(){
  230. this.$el.html(this.template());
  231. this.fileUpload.dropZone = this.$(".targetarea");
  232. this.$("#fileupload").fileupload(this.fileUpload);
  233. this.delegateEvents();
  234. },
  235. uploadAdd : function FileUploadView_uploadAdd(e,data){
  236. for (var i = 0, ii = data.files.length; i < ii; i++) {
  237. var file = this.collection.findWhere({
  238. name : data.files[i].name,
  239. size : data.files[i].size
  240. });
  241. if (file === undefined) {
  242. file = new FileModel({
  243. name: data.files[i].name,
  244. size: data.files[i].size,
  245. status: "added",
  246. editableProperties : this.options.editableProperties,
  247. saveURL : this.options.saveURL
  248. });
  249. this.collection.add([ file ]);
  250. file.set("jqXHR",data.submit());
  251. }
  252. }
  253. },
  254. uploadSubmit : function FileUploadView_uploadSubmit(e,data){
  255. var file = this.collection.findWhere({
  256. name : data.files[0].name,
  257. size : data.files[0].size
  258. });
  259. if (file){
  260. data.formData = serializeFormByPrefix("default",this.options.defaultProperties);
  261. data.formData.uploaderID = this.options.uploaderID;
  262. data.formData.fileID = this.options.fileIDs.pop();
  263. file.set("fileID",data.formData.fileID);
  264. }
  265. },
  266. uploadSend : function FileUploadView_uploadSend(e,data){
  267. var file = this.collection.findWhere({
  268. name : data.files[0].name,
  269. size : data.files[0].size
  270. });
  271. if (file) {
  272. file.set({
  273. status : "uploading",
  274. progress : {
  275. loaded : 0,
  276. total : 0,
  277. bitrate : 0
  278. }
  279. });
  280. }
  281. },
  282. uploadProgress : function FileUploadView_uploadProgress(e,data){
  283. var file = this.collection.get(data.formData.fileID);
  284. if (file){
  285. file.set({
  286. status : "uploading",
  287. progress : data._progress
  288. });
  289. }
  290. },
  291. uploadDone : function FileUploadView_uploadDone(e,data){
  292. var file = this.collection.findWhere({
  293. fileID : data.formData.fileID
  294. });
  295. if (file && data.result.error){
  296. file.set({
  297. status : "failed",
  298. error : data.result.error,
  299. jqXHR : undefined
  300. });
  301. }
  302. else if (data.result.error && Window.app && Window.app.errorCollection){
  303. Window.app.errorCollection.add({
  304. message : data.result.error.message,
  305. error: data.result.error
  306. });
  307. }
  308. else if (file){
  309. for (var i=0, ii=data.result.fileIDs.length; i<ii; i++)
  310. this.options.fileIDs.unshift(data.result.fileIDs[i]);
  311. file.set({
  312. jqXHR : undefined,
  313. status : "uploaddone",
  314. uploadProgress : undefined
  315. });
  316. }
  317. },
  318. uploadFail : function FileUploadView_uploadFail(e,data){
  319. var file = this.collection.findWhere({
  320. name : data.files[0].name,
  321. size : data.files[0].size
  322. });
  323. if (file){
  324. file.set({
  325. jqXHR : undefined,
  326. status : "failed",
  327. error : data._response.errorThrown,
  328. uploadProgress : undefined
  329. });
  330. }
  331. else{
  332. this.options.generalErrors.add({
  333. error: {
  334. message: data._response.errorThrown
  335. }
  336. });
  337. }
  338. },
  339. dragEnter: function FileUploadView_dragEnter(){
  340. this.$(".targetarea").addClass("dragover");
  341. if (this.stopShow) {
  342. clearTimeout(this.stopShow);
  343. this.stopShow = 0;
  344. }
  345. },
  346. dragOver: function FileUploadView_dragOver(){
  347. this.$(".targetarea").addClass("dragover");
  348. if (this.stopShow) {
  349. clearTimeout(this.stopShow);
  350. this.stopShow = 0;
  351. }
  352. },
  353. dragLeave : function FileUploadView_dragLeave(){
  354. var self = this;
  355. this.stopShow = setTimeout(function(){
  356. self.$(".targetarea").removeClass("dragover");
  357. },200);
  358. }
  359. });
  360. ErrorModel = Backbone.Model.extend();
  361. ErrorCollection = Backbone.Collection.extend({
  362. model : ErrorModel
  363. });
  364. ErrorView = Backbone.View.extend({
  365. initialize : function ErrorView_initialize(options){
  366. this.template = Handlebars.compile(Backbone.$("#general-error-template").html());
  367. this.listenTo(this.model,"change",this.render,this);
  368. },
  369. events : {
  370. "click .remove" : "removeError",
  371. "click .icon-info" : "showInfo"
  372. },
  373. render : function Error_render(){
  374. this.$el.html(this.template(this.model.attributes));
  375. },
  376. removeFile : function ErrorView_removeFile(){
  377. // remove file from memory and DOM
  378. this.collection.remove(this.model);
  379. this.remove();
  380. delete this.model;
  381. },
  382. showInfo : function ErrorView_showInfo(){
  383. this.$(".info").toggle();
  384. },
  385. });
  386. ErrorCollectionView = Backbone.View.extend({
  387. initialize : function ErrorCollectionView_initialize(options){
  388. this.listenTo(this.collection,"add",this.addError,this);
  389. },
  390. addError : function ErrorCollectionView_addFile(error){
  391. var errorView = new ErrorView({
  392. model : error
  393. });
  394. this.$el.append(errorView.el);
  395. errorView.render();
  396. }
  397. });
  398. /* FORMATTING HELPERS */
  399. function formatFileSize(bytes) {
  400. if (typeof bytes !== 'number') {
  401. return '';
  402. }
  403. if (bytes >= 1000000000) {
  404. return (bytes / 1000000000).toFixed(2) + ' GB';
  405. }
  406. if (bytes >= 1000000) {
  407. return (bytes / 1000000).toFixed(2) + ' MB';
  408. }
  409. return (bytes / 1000).toFixed(2) + ' KB';
  410. };
  411. Handlebars.registerHelper('filesize', safeStringify(formatFileSize));
  412. function formatBitRate(bits) {
  413. if (typeof bits !== 'number') {
  414. return '';
  415. }
  416. if (bits >= 1000000000) {
  417. return (bits / 1000000000).toFixed(2) + ' Gbit/s';
  418. }
  419. if (bits >= 1000000) {
  420. return (bits / 1000000).toFixed(2) + ' Mbit/s';
  421. }
  422. if (bits >= 1000) {
  423. return (bits / 1000).toFixed(2) + ' kbit/s';
  424. }
  425. return bits.toFixed(2) + ' bit/s';
  426. }
  427. Handlebars.registerHelper('bitrate', safeStringify(formatBitRate));
  428. function formatPercentage(value,total) {
  429. return Math.floor(value / total * 100).toString() + '%'
  430. }
  431. Handlebars.registerHelper('percentage', safeStringify(formatPercentage));
  432. function syntaxHighlight(json) {
  433. if (typeof json != 'string') {
  434. json = JSON.stringify(json, undefined, 2);
  435. }
  436. json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  437. return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
  438. var cls = 'number';
  439. if (/^"/.test(match)) {
  440. if (/:$/.test(match)) {
  441. cls = 'key';
  442. } else {
  443. cls = 'string';
  444. }
  445. } else if (/true|false/.test(match)) {
  446. cls = 'boolean';
  447. } else if (/null/.test(match)) {
  448. cls = 'null';
  449. }
  450. return '<span class="' + cls + '">' + match + '</span>';
  451. });
  452. }
  453. Handlebars.registerHelper('syntaxhighlight',safeStringify(syntaxHighlight));
  454. function safeStringify(fn){
  455. return function(){
  456. return new Handlebars.SafeString(fn.apply(this,Array.prototype.slice.call(arguments)));
  457. };
  458. };
  459. /* JQUERY HELPERS */
  460. jQuery.fn.block = function(){
  461. var thispos = this.offset(), blocker = "";
  462. if (this.data("blocker"))
  463. blocker = this.data("blocker");
  464. else
  465. blocker = jQuery("<div style='position:absolute;background-color:#333333;padding:10px 20px;border:1px solid #000000;'><img src='/webtop/images/loading-dark.gif' alt='Loading...' width='54' height='55'></div>");
  466. blocker.css({
  467. top : thispos.top,
  468. left : thispos.left,
  469. width : this.width(),
  470. height : this.height(),
  471. opacity : 0.5
  472. })
  473. .find("img").css({
  474. position : "absolute",
  475. top : thispos.top + (this.height()-55) / 2,
  476. left : thispos.left + (this.width()-54) / 2
  477. }).end()
  478. .appendTo(jQuery("body"));
  479. this.data("blocker",blocker);
  480. return blocker;
  481. };
  482. jQuery.fn.unblock = function(){
  483. if (this.data("blocker")){
  484. this.data("blocker").remove();
  485. this.data("blocker",null);
  486. }
  487. return this;
  488. };
  489. function serializeFormByPrefix(prefix,properties,includeprefix){
  490. var inputs = "", tmp = {}, data = {};
  491. properties = Array.prototype.slice.call(properties);
  492. if (properties.indexOf("ObjectID")===-1)
  493. properties.push("ObjectID");
  494. if (properties.indexOf("Typename")===-1)
  495. properties.push("Typename");
  496. includeprefix = includeprefix === true;
  497. // get the post values
  498. for (var i=0; i<properties.length; i++){
  499. inputs = jQuery('input[name='+prefix+properties[i]+']:not([type]),input[name='+prefix+properties[i]+'][type=text],input[name='+prefix+properties[i]+'][type=checkbox]:checked,input[name='+prefix+properties[i]+'][type=radio]:checked,input[name='+prefix+properties[i]+'][type=hidden],select[name='+prefix+properties[i]+'],textarea[name='+prefix+properties[i]+']');
  500. if (inputs.size()) {
  501. tmp[properties[i]] = [];
  502. inputs.each(function(){
  503. var self = jQuery(this);
  504. if ((!(self.is("[type=radio]") || self.is("[type=radio]")) || self.is(":checked")) && self.val()!=="")
  505. tmp[properties[i]].push(self.val());
  506. });
  507. data[(includeprefix ? prefix : "")+properties[i]] = tmp[properties[i]].join();
  508. }
  509. }
  510. data["FarcryFormPrefixes"] = prefix;
  511. data["FarcryFormSubmitted"] = "";
  512. data["FarcryFormSubmitButton"] = "Submit";
  513. return data;
  514. }