1/*************************************************************************** 2 * 3 * Cargo Namespace 4 * 5 **************************************************************************/ 6var Cargo = { }; 7 8// Create a default objects 9Cargo.o = { }; // Default object definition (for backbone init) 10Cargo.o.model = { }; // Default object definition (for backbone init) 11Cargo.o.collection = { }; // Default object definition (for backbone init) 12Cargo.o.view = { }; // Default object definition (for backbone init) 13 14Cargo.Router = { }; // All main router methods 15Cargo.Template = { }; // Template controllers 16Cargo.Helper = { }; // Helper methods 17Cargo.API = { }; // API communication 18Cargo.Event = { }; // Backbone events (History, close, etc) 19 20Cargo.Collection = { }; // Collections base 21Cargo.Collection.Page = { }; // Project collection pages (for pagination in memory) 22Cargo.Collection.NavPage = { }; // Navigation collection pages (for pagination in memory) 23Cargo.Model = { }; // Models base 24Cargo.View = { }; // Views base 25 26/** 27 * Action namespace 28 */ 29var Action = window.Action || { }; 30 31 /*************************************************************************** 32 * 33 * Cargo core functionality 34 * This contains all core functionality native to all Cargo websites 35 * For the most part, these methods are reproductions of the existing 36 * functionality from site.package 37 * 38 **************************************************************************/ 39 40/* 41 - Slideshow : _cargo/_core/slideshow.js 42 - Handlebars : _cargo/_core/handlebars.js 43 - Fullscreen : freshbox jQuery plugin in this file 44 - Lightbox : freshbox jQuery plugin in this file 45 - Keyboard nav : this file 46 - Video player : _cargo/_core/video.js 47 - Log : this file 48 */ 49 50 Cargo.Core = { 51 52 /** 53 * Make a safe logging method 54 */ 55 log : function(args) { 56 if (typeof console == 'undefined' || !window.console) { 57 return; 58 } 59 console.log(args); 60 }, 61 62 /** 63 * Keyboard shortcuts 64 * Add and Remove 65 * Add Usage: 66 * Cargo.Core.KeyboardShortcut.Add("Shift+F", 70, function(){ }); 67 * Remove Usage: 68 * Cargo.Core.KeyboardShortcut.Remove("Shift+F"); 69 */ 70 KeyboardShortcut : { 71 Add : function(key, keycode, fn) { 72 var data = { 73 'type' : 'keydown', 74 'disable_in_input' : true, 75 'keycode' : keycode 76 }; 77 78 shortcut.add(key, fn, data); 79 }, 80 81 Remove : function(key) { 82 shortcut.remove(key); 83 }, 84 85 /* 86 * Base set of keyboard shortcuts 87 */ 88 InitDefaults : function() { 89 this.Add("J", 74, function() { Action.Project.Next() }); 90 this.Add("K", 75, function() { Action.Project.Prev() }); 91 this.Add("R", 82, function() { Action.Project.Random() }); 92 this.Add("X", 88, function() { Action.Project.Index() }); 93 this.Add("I", 73, function() { Action.Project.Index() }); 94 95 this.Add("Shift+F", 70, function() { 96 if ( ! $("body").hasClass("rail_active") ) { 97 Cargo.app.Rail.Events.trigger("show_rail_following"); 98 } else { 99 Cargo.app.Rail.Events.trigger("rail:hide"); 100 } 101 }); 102 this.Add("Shift+G", 71, function() { Cargo.Helper.GoToUrl("images"); }); 103 104 this.Add("Shift+A", 65, function() { 105 if(typeof require == "function" && Cargo.API.Config.is_editor) { 106 if(parent.location == window.location) { 107 Cargo.Event.trigger("add_history", "admin"); 108 } 109 } else { 110 Cargo.Helper.GoToUrl("admin"); 111 } 112 }); 113 114 this.Add("Shift+E", 69, function() { 115 if(Cargo.Model.Site.GetIsEditor() && Cargo.Model.Project.GetId() && parent.location == window.location) { 116 Cargo.Helper.GoToUrl("admin/"+Cargo.Model.Project.GetId()); 117 } }); 118 119 this.Add("Shift+H", 72, function() { Cargo.Helper.GoToUrl("admin/html"); }); 120 this.Add("Shift+C", 67, function() { Cargo.Core.loadInspector3(); }); 121 this.Add("Shift+D", 68, function() { Cargo.Helper.GoToUrl("admin/designs"); }); 122 this.Add("Shift+X", 88, function() { Cargo.Helper.GoToUrl(""); }); 123 this.Add("Shift+I", 73, function() { loadInspector(); }); 124 125 this.Add("Esc", 27, function() { 126 $("a.follow_popup_cancel").click(); 127 if ( ! $("#freshbox").length > 0 ) { 128 Cargo.app.Rail.Events.trigger("rail:hide"); 129 } 130 }); 131 132 } 133 }, 134 135 loadEditor: function(){ 136 var iframe = document.createElement('iframe'); 137 138 //Cargo.Rail.Init = function(){}; 139 140 141 iframe.setAttribute('id', 'content_frame'); 142 143 iframe.src = Cargo.Helper.GetBaseUrl(true, true); 144 iframe.frameBorder = '0'; 145 iframe.style.display = 'none'; 146 iframe.style.width = 'calc(100% - 400px)'; 147 iframe.style.height = '100%'; 148 iframe.style.left = '400px'; 149 iframe.style.left = '0'; 150 iframe.style.top = '0'; 151 iframe.style.position = 'fixed'; 152 153 var nodesToRemove = [], 154 i; 155 156 for(i = 0; i < document.body.childNodes.length; i++){ 157 if(document.body.childNodes[i] !== iframe){ 158 nodesToRemove.push(document.body.childNodes[i]); 159 } 160 } 161 162 [].forEach.call(document.querySelectorAll('link[rel="stylesheet"], style'), function(node){ 163 nodesToRemove.push(node); 164 }); 165 166 require(["/_jsapps/editor/main.bootstrap.js"], function() { 167 168 }); 169 170 iframe.onload = function(){ 171 172 iframe.style.display = ''; 173 174 var html = document.getElementsByTagName("html")[0]; 175 176 if(html !== undefined){ 177 html.removeAttribute('style'); 178 } 179 180 nodesToRemove.forEach(function(node){ 181 if(node.parentNode !== null){ 182 node.parentNode.removeChild(node); 183 } 184 }); 185 186 } 187 188 document.body.appendChild(iframe); 189 }, 190 191 /** 192 * Replace the old loading animations with retnified versions 193 * 194 * Note: This sits in a timeout because it won't fire correctly 195 * on doc.ready 196 */ 197 ReplaceLoadingAnims : { 198 init : function(specificSelection) { 199 // First, check for existing spinners 200 $(".retinaSpinner").each(function() { 201 if (window.devicePixelRatio > 1 ){ 202 $(this).addClass('rotating'); 203 } 204 }); 205 206 setTimeout(function() { 207 if(Cargo.Helper && !Cargo.Helper.isOldIE()){
208 var spinnerEl = $('<div></div>'); 209 210 for(var i = 0; i < 8; i++){ 211 spinnerEl.append( 212 '<div style="position: absolute; top: -2px;">' + 213 '<div class="spinner_circle part' + (i + 1) + '" style="-webkit-transform: rotate(' + (i * 45) +'deg) translate(6px, 0px); transform: rotate(' + (i * 45) +'deg) translate(6px, 0px);"></div>' + 214 '</div>'); 215 } 216 217 if(typeof specificSelection == "undefined" && $('img[src$="/loadingAnim.gif"]').length > 0){ 218 $('img[src$="/loadingAnim.gif"]').each(function(){ 219 Cargo.Core.ReplaceLoadingAnims.replace(this, spinnerEl); 220 }); 221 } else if(typeof specificSelection != "undefined") { 222 specificSelection.each(function(){ 223 Cargo.Core.ReplaceLoadingAnims.replace(this, spinnerEl); 224 }); 225 } 226 227 Cargo.Event.trigger("loading_anims_replaced"); 228 } 229 }, 100); 230 }, 231 232 replace : function(which, spinnerEl) { 233 var thisSpinner = spinnerEl.clone(); 234 235 thisSpinner.attr('class', 'retinaSpinner ' + $(which).attr('class')); 236 thisSpinner.attr('id', $(which).attr('id')); 237 thisSpinner.attr('style', $(which).attr('style')); 238 thisSpinner.attr('data-loading', $(which).attr('data-loading')); 239 240 if (window.devicePixelRatio > 1 ){ 241 thisSpinner.addClass('rotating'); 242 } 243 244 // replace the old GIF 245 $(which).replaceWith(thisSpinner); 246 } 247 }, 248 249 /** 250 * This method is called by events.js on project_load_complete 251 * It loads the template, and loads the view into Memory 252 * The view for the freshbox handles the click events 253 */ 254 CheckAndInitFreshbox : function() { 255 // Remove the fullscreen from offline mode 256 if( Cargo.API.Config.offline ) { 257 return false; 258 } 259 260 /* Check for a fullscreen button perpare it */ 261 if( $("#fullscreen").length > 0 || Cargo.Model.DisplayOptions.GetLightboxView() ) { 262 /* Prevent multiple instantiation */ 263 if(typeof Cargo.View.Fullscreen == "undefined") { 264 /* Add the template to the stack */ 265 Cargo.Template.Add( "Freshbox" ); 266 267 /* Make sure we have loaded the freshbox */ 268 Cargo.Template.Load( "Freshbox", Cargo.API.GetFreshboxPath() ); 269 270 /* Initialize the fullscreen view */ 271 Cargo.View.Fullscreen = new Cargo.o.view.Freshbox({ 272 el : "body", 273 model : Cargo.Model.Project 274 }); 275 } 276 } 277 278 }, 279 280 /** 281 * This finds the search data-view and loads the search view 282 * if one is not already loaded 283 */ 284 FindAndLoadSearch : function(first_load) { 285 if(typeof Cargo.View.Search != "undefined" && !first_load) { 286 return false; 287 } 288 289 if($("[data-view='search']").length > 0) { 290 Cargo.View.Search = new Cargo.o.view.Search({ 291 el : "[data-view='search']" 292 }); 293 294 } else if($("#search_form").length > 0) { 295 Cargo.View.Search = new Cargo.o.view.Search({ 296 el : "#search_form" 297 }); 298 } 299 }, 300 301 /** 302 * This method is meant to reseed the project data, rather then refreshing the whole page 303 * This is called from the admin frameset upon project ordering and toggle 304 */ 305 ReseedProjectModels : function() { 306 // Reset the page and offset count 307 Cargo.API.Config.current_offset = 0; 308 Cargo.API.Config.current_page = 0; 309 Cargo.API.Config.site_recently_created = false; 310 311 // Get the URL of the API path 312 Cargo.Collection.Navigation.url = Cargo.API.GetNavigationCargoPath( 0 ); 313 Cargo.Collection.Projects.url = Cargo.API.GetProjectsPath( 0 ); 314 315 // Fetch the data 316 Cargo.Collection.Projects.fetch({reset: true, complete: function() { 317 Cargo.Event.Common(); 318 Cargo.Event.trigger("reseed_project_complete"); 319 }}); 320 Cargo.Collection.Navigation.fetch({reset: true, complete: function() { 321 Cargo.Event.trigger("reseed_navigation_complete"); 322 }}); 323 324 }, 325 326 loadInlineEditor: function(){ 327 328 console.log('SIKE!'); 329 330 /*$('head').append( $('<link rel="stylesheet" type="text/css" />').attr('href', '/_jsapps/inline-editor/cargoEditor.css') ); 331 $('head').append( $('<link rel="stylesheet" type="text/css" />').attr('href', '/_jsapps/inline-editor/cargoEditor_plugins_all.css') ); 332 333 $.getScript( "/_jsapps/inline-editor/cargoEditor.js", function( data, textStatus, jqxhr ) { 334 335 $('head').append( $('<script/>').attr('src', '/_jsapps/inline-editor/cargoEditor_plugins_all.js') ); 336 337 // attach editors to projects. 338 $('[data-ce-host="true"]').each(function() { 339 if (!$(this).is('[contenteditable]')) { 340 CargoEditor.addEditor(this); 341 } 342 }); 343 344 });*/ 345 346 347 },
348 349 loadInspector3 : function(){ 350 351 if(window.editor !== undefined){ 352 return false; 353 } 354 355 if(Cargo.API.Config.network == "Persona") { 356 Cargo.Helper.GoToUrl("/admin/design/type-layout/css"); 357 return; 358 } 359 360 Cargo.Core.Inspector3.load({ 361 362 onInit: function(inspector){ 363 364 function addClickBehavior(){ 365 366 // replace all click events handled by Backbone with dblClick events. 367 _.each(Cargo.View, function(view){ 368 if(typeof view.undelegateEvents == "undefined") { 369 return; 370 } 371 372 // remove all events bound to this view 373 view.undelegateEvents(); 374 375 _.each(view.events, function(method, key){ 376 var match = key.match(delegateEventSplitter); 377 378 if(match !== null) { 379 // found a click event. Add a dblclick in place. 380 if(match[1] === "click") { 381 view.events['dblclick ' + match[2]] = method; 382 delete view.events[key]; 383 } 384 } 385 }); 386 387 // re-attached all events including the modified ones. 388 view.delegateEvents(); 389 }); 390 391 $("a:not([rel], #splash)").unbind("click").off('dblclick').on("dblclick", function() { 392 393 var href = this.getAttribute('href'); 394 395 if(href !== null && href !== '#' && href !== '' && href.indexOf('javascript:') === -1){ 396 397 var cargoURL = (Cargo.hasOwnProperty('Helper') ? Cargo.Helper.GetUrl() : Cargo.Config.GetCookieUrl()), 398 customDomain = inspector.options.coreWindow.location.hostname.search(/cargocollective\.com/i) === -1, 399 sameDomain = (this.hostname.search(/cargocollective\.com/i) !== -1 ? this.pathname.split('/')[1] === cargoURL : this.hostname === inspector.options.coreWindow.location.hostname), 400 subLocation = this.pathname.split('/')[(customDomain ? 1 : 2)], 401 forbiddenSubLocation = (subLocation === undefined ? false : subLocation.search(/^(following|admin|images)$/i) !== -1); 402 403 //console.log(cargoURL, customDomain, subLocation, sameDomain, forbiddenSubLocation); 404 405 if(sameDomain && !forbiddenSubLocation) { 406 inspector.options.coreDocument.location.href = this.getAttribute('href'); 407 } else { 408 var newTab = inspector.options.coreWindow.open(href, '_blank'); 409 newTab.focus(); 410 } 411 412 } 413 }); 414 415 $('img').unbind('click'); 416 417 if(Cargo.Core.Slideshow.hasOwnProperty('SlideshowObjects')){ 418 $.each(Cargo.Core.Slideshow.SlideshowObjects, function(key, val){ 419 val.elements.$thumbnails.on('dblclick', function(){ 420 val.hideThumbnails(); 421 val.showContainer(); 422 }); 423 }) 424 } 425 426 } 427 428 var $ = inspector.options.coreWindow.$, 429 Cargo = inspector.options.coreWindow.Cargo, 430 delegateEventSplitter = /^(\S+)\s*(.*)$/; 431 432 setTimeout(function(){ 433 Cargo.app.Rail.Events.on("toolset:loaded", function() { 434 $("#toolset, #toolset *, #toolset_menu, #toolset_menu *, #design_panel, #design_panel *").unbind().off(); 435 $("#toolset_follow_pair a, #toolset_follow a").unbind(); 436 437 $("#toolset").fadeTo('fast', 0.3).css('pointer-events', 'none'); 438 439 }); 440 441 $("#toolset, #toolset *, #toolset_menu, #toolset_menu *, #design_panel, #design_panel *").unbind().off(); 442 $("#toolset_follow_pair a, #toolset_follow a").unbind(); 443 $("#toolset").fadeTo('fast', 0.3).css('pointer-events', 'none'); 444 445 }, 500); 446 447 $("#toolset").fadeTo('fast', 0.3).css('pointer-events', 'none'); 448 449 450 Cargo.Event.on("pagination_complete", function( new_page ) { 451 addClickBehavior(); 452 }); 453 454 Cargo.Event.on("project_load_complete", function( new_page ) { 455 addClickBehavior(); 456 }); 457 458 $('.slideshow *').unbind().off(); 459 460 addClickBehavior(); 461 } 462 }); 463 }, 464 465 /** 466 * Loads the inspector 467 */ 468 LoadInspector : function() { 469 470 // 302'd 471 this.loadInspector3(); 472 473 }, 474 475 HiRes : { 476 active : false, 477 478 Queue : { 479 STATE_LOADING: 0, 480 STATE_COMPLETE: 1, 481 STATE_ERROR: -1, 482 ID: 'hi-res-queue', 483 FILE_EXTENSION: /(?:\.([^.]+))?$/, 484 FILE_NAME: /(?:\/([^\/]+))?$/, 485 element: null, 486 487 init: function() { 488 $('body').append('<div id="' + this.ID + '" style="display: none;" />'); 489 this.element = $('#' + this.ID); 490 }, 491 492 search: function() { 493 if ( ! Cargo.Core.HiRes.active) { 494 return; 495 } 496 497 // $('[data-mid], div.thumb_image img').map(function() { 498 $('[data-hi-res]').map(function() { 499 // @TODO Determine means to ignore slideshow thumbnails? 500 if (this.nodeName === 'IMG') { 501 Cargo.Core.HiRes.Queue.push(this); 502 } 503 }); 504 }, 505 506 push: function(img) { 507 var img_src = img.src 508 // var hi_res_src = img_src.replace(this.FILE_EXTENSION, '_2x.$1'); 509 var hi_res_src = $(img).data('hi-res'); 510 var mid = $(img).data('mid'); 511 // Hack to support thumbnails 512 var queue_id = mid ? 'hi-res-queue-' + mid : 'hi-res-queue-' + (img_src.match(this.FILE_NAME)[1]).replace(/[\W\.(_2x)]/g, ''); 513 var queue_img = $('#' + queue_id); 514 515 if (queue_img.length) { 516 switch (queue_img.attr('data-hi-res-status')) { 517 case this.STATE_COMPLETE: 518 return this.handleCachedImage(queue_img, img); 519 case this.STATE_LOADING: 520 return this.handleDuplicateImage(queue_img, img); 521 default: 522 return; 523 } 524 } else { 525 this.element.append('<img id="' + queue_id + '" data-hi-res-status="' + this.STATE_LOADING + '">'); 526 queue_img = $('#' + queue_id); 527 queue_img.data('lo-res-images', [img]); 528 queue_img.one('load', function() { 529 Cargo.Core.HiRes.Queue.handleLoad(queue_img); 530 }).one('error', function() { 531 Cargo.Core.HiRes.Queue.handleError(queue_img); 532 }).attr('src', hi_res_src); 533 } 534 }, 535 536 handleCachedImage: function(queue_img, img) { 537 img.src = queue_img.attr('src'); 538 }, 539 540 handleDuplicateImage: function(queue_img, img) { 541 var lo_res_images = queue_img.data('lo-res-images'); 542 lo_res_images.push(img); 543 queue_img.data('lo-res-images', lo_res_images); 544 }, 545 546 handleLoad: function(queue_img) { 547 var hi_res_src = queue_img.attr('src'); 548 queue_img.attr('data-hi-res-status', this.STATE_COMPLETE); 549 queue_img.data('lo-res-images').map(function(img) { 550 img.src = hi_res_src; 551 }); 552 }, 553 554 handleError: function(queue_img) { 555 queue_img.attr('data-hi-res-status', this.STATE_ERROR); 556 } 557 }, 558 559 Loader : { 560 init: function() {
561 checkPixelRatio(); 562 563 function checkPixelRatio() { 564 var prevDevicePixelRatio = parseFloat($.cookie('userPixelRatio')); 565 566 if(prevDevicePixelRatio != window.devicePixelRatio) { 567 if(window.devicePixelRatio > 1.5) { 568 enableHiRes(); 569 } else { 570 disableHiRes(); 571 } 572 } 573 574 $.cookie('userPixelRatio', window.devicePixelRatio, {path: '/'}); 575 } 576 577 function disableHiRes() { 578 $.post("/dispatch/cargosite/cargoUseHiRes", { use : false }, function() {}, "json"); 579 } 580 581 function enableHiRes() { 582 $.post("/dispatch/cargosite/cargoUseHiRes", { use : true }, function() {}, "json"); 583 584 $(document).bind("projectLoadComplete", function(e, pid) { 585 Cargo.Core.HiRes.Queue.search(); 586 }); 587 588 $(document).bind("pageChangeComplete", function(e, newpage) { 589 Cargo.Core.HiRes.Queue.search(); 590 }); 591 592 $(document).bind("paginationComplete", function(e) { 593 Cargo.Core.HiRes.Queue.search(); 594 }); 595 596 Cargo.Core.HiRes.active = true; 597 Cargo.Core.HiRes.Queue.init(); 598 Cargo.Core.HiRes.Queue.search(); 599 } 600 601 // listen to changes while page is loaded 602 try { 603 var MediaQueryList = window.matchMedia("(-webkit-min-device-pixel-ratio: 1.5)"); 604 605 if (typeof MediaQueryList.addListener === "function") { 606 MediaQueryList.addListener(function(){
607 checkPixelRatio(); 608 }); 609 } 610 } catch(err) { 611 // matchMedia does not exist 612 } 613 } 614 } 615 616 }, 617 618 EU_cookie_notification : function() { 619 if($('head meta[name="en_cookie_notification"]').length > 0) { 620 if(!localStorage.getItem('eu_cookie_notification')) { 621 var class_name = $('head meta[name="en_cookie_notification"]').attr('position'), 622 content = $('head meta[name="en_cookie_notification"]').attr('description'), 623 link = $('head meta[name="en_cookie_notification"]').attr('policy_link'); 624 content = content.replace('[policy_link]', '<a href="'+link+'" target="_blank">').replace('[/policy_link]','</a>'); 625 626 $('body').prepend('<div id="eu_cookie_notification" class="'+class_name+'">'+content+'</div>'); 627 628 $("#eu_cookie_notification").click(function(e) { 629 if($(e.target).is('a')){ 630 return true; 631 } 632 633 $(this).remove(); 634 localStorage.setItem('eu_cookie_notification', 'true'); 635 }); 636 } 637 } 638 } 639 640 641 642 }; 643 644 645 646 647Cargo.o.analytics = Backbone.View.extend({ 648 649 enabled: true, 650 651 Config: { 652 id: 'UA-1837333-7', 653 name: '__cargo_analytics', 654 }, 655 656 initialize: function() { 657 658 if(Cargo.API) { 659 660 this.enabled = Cargo.API.GetUseGoogleAnalytics(); 661 662 if (Cargo.API.Config.is_domain) { 663 this.Config.custom_url = '/' + document.location.host; 664 } 665 666 if(Cargo.API.Config.network == "Persona") { 667 this.Config.id = 'UA-1837333-14'; 668 } 669 } else { 670 this.Config.id = 'UA-1837333-14'; 671 } 672 673 if(this.enabled) { 674 675 // Include analytics.js 676 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 677 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 678 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 679 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 680 681 // Create Google Analytics tracker, send initial pageview 682 ga('create', this.Config.id, 'auto', {'name': this.Config.name, 'cookieName': this.Config.name}); 683 684 this.pageview(false); 685 686 } 687 688 }, 689 690 pageview: function(send_to_all_trackers, url) { 691 692 693 var tracker = this.Config.name, 694 pathname = document.location.pathname.replace(/\/$/, ''); 695 696 697 if (typeof url !== 'undefined' && ! send_to_all_trackers) { 698 699 if(this.enabled) { 700 701 ga(this.Config.name + '.send', 'pageview', { 702 'page': url, 703 'dimension3': 'Cargo 1' 704 }); 705 706 } 707 708 return; 709 } 710 711 if(this.enabled) { 712 713 if (typeof this.Config.custom_url !== 'undefined') { 714 ga(this.Config.name + '.send', 'pageview', { 715 'page': this.Config.custom_url + pathname, 716 'dimension3': 'Cargo 1' 717 }); 718 } else { 719 ga(this.Config.name + '.send', 'pageview', { 720 'dimension3': 'Cargo 1' 721 }); 722 } 723 724 } 725 726 // Send data for any other trackers 727 if (typeof ga == 'function' && send_to_all_trackers) { 728 ga(function() { 729 ga.getAll().map(function(g) { 730 var t = g.get('name'); 731 if (t != tracker) { 732 ga(t + '.send', 'pageview'); 733 } 734 }); 735 }); 736 737 // Push data to legacy trackers 738 if (typeof _gaq !== 'undefined') { 739 _gaq.push(['_trackPageview', pathname]); 740 } 741 } 742 } 743}); 744 745 746/*************************************************************************** 747 * 748 * API 749 * It contains all methods related to the API 750 * 751 **************************************************************************/ 752 753/** 754 * @namespace API 755 * @memberOf Cargo 756 * @extends {Backbone.View} 757 */ 758 759Cargo.o.api = Backbone.View.extend({ 760 761 /** 762 * Set the config values for the API 763 * 764 * @private 765 * 766 * @memberOf Cargo.API 767 * @example Cargo.API.Config.api_path; 768 */ 769 Config : { 770 Version : "v0", 771 api_path : "/_api", 772 cargo_id : "", 773 cargo_url : "", 774 cargo_url_override : null, 775 filter : "", 776 search_term : "", 777 route_name : "", 778 template_basename : "design", 779 current_offset : 0, // Set in the defaults which is loaded on page load 780 current_page : 1, // Set in the defaults which is loaded on page load 781 start_project : null, 782 is_filter : false, 783 is_search : false, 784 is_editor : false, 785 is_solo : false, 786 is_direct_link : false, 787 is_custom_route : false, 788 admin_edit_project : false, 789 first_load_finished : false 790 }, 791 792 /** 793 * Load the API defaults by extending the Config vars using underscore 794 * 795 * @private 796 * 797 * @memberOf Cargo.API 798 */ 799 initialize : function() { 800 _.extend(this.Config, $.parseJSON( $("script[data-set='defaults']").html() )); 801 }, 802 803 /** 804 * returns the raw JSON data, just in case you need it 805 * 806 * @private 807 * 808 * @memberOf Cargo.API 809 * 810 * @param {string} [path] The path to the API endpoint 811 * @param {function} [callback] Callback function to handle return value 812 * 813 * @example Cargo.API.fetch(Cargo.API.GetSitePath(), myCallback); 814 */ 815 fetch : function( path, callback ) { 816 $.getJSON( path, callback ); 817 }, 818 819 GetUseGoogleAnalytics: function(){ 820 821 return this.Config._use_google_analytics == 1; 822 823 }, 824 825 /* 826 * ###################################################################### 827 * 828 * PATHS 829 * 830 * ###################################################################### 831 */ 832 /** 833 * Gets the path for the Site API Model 834 * 835 * @public 836 * 837 * @memberOf Cargo.API 838 * 839 * @return {string} 840 * @example Cargo.API.GetSitePath(); 841 */ 842 GetSitePath : function() { 843 var c = this.Config; // shorthand 844 var path = c.api_path + "/" + c.Version + "/site/" + c.cargo_url; 845 return path; 846 }, 847 848 /** 849 * Gets the path for the Design API Model 850 * 851 * @public 852 * 853 * @memberOf Cargo.API 854 * 855 * @return {string} 856 * @example Cargo.API.GetDesignPath(); 857 */ 858 GetDesignPath : function() { 859 var c = this.Config; // shorthand 860 var url = (c.cargo_url_override) ? c.cargo_url_override : c.cargo_url; 861 var path = c.api_path + "/" + c.Version + "/designs/" + url; 862 return path; 863 }, 864 865 /** 866 * Gets the path for the Files API 867 * 868 * @public 869 * 870 * @memberOf Cargo.API 871 * 872 * @param {string} [file] The name of the file 873 * @return {string} 874 * @example Cargo.API.GetDesignFile(file); 875 */ 876 GetDesignFile : function(file) { 877 var path = this.GetDesignPath() + "?file=" + file; 878 return path; 879 }, 880 881 /** 882 * Gets the path for the Template files 883 * 884 * @public 885 * 886 * @memberOf Cargo.API 887 * 888 * @return {string} 889 * @example Cargo.API.GetTemplatePath(); 890 */ 891 GetTemplatePath : function() { 892 var c = this.Config; // shorthand 893 var route_name = (c.route_name) ? c.route_name + "/" : ""; 894 return this.GetDesignFile(".templates/" + c.template_basename + "/" + route_name); 895 }, 896 897 /** 898 * Gets the path for the Fullsreen handlebars 899 * 900 * @public 901 * 902 * @memberOf Cargo.API 903 * 904 * @return {string} 905 * @example Cargo.API.GetFreshboxPath(); 906 */ 907 GetFreshboxPath : function() { 908 return '/_jsapps/api/_templates/Freshbox.handlebars'; 909 }, 910 911 /** 912 * Gets the design path + a file 913 * optionally adds the "to_var" param to pass to the api 914 * 915 * @public 916 * 917 * @memberOf Cargo.API 918 * 919 * @param {string} [file] The name of the file requested 920 * @param {boolean} [to_var] Optional value to return the content as a javascript var 921 * @return {string} 922 * @example Cargo.API.GetDesignAndFile(file, to_var); 923 */ 924 GetDesignAndFile : function(file, to_var) { 925 var file_path = this.GetTemplatePath() + file; 926 if( to_var ) { 927 file_path += "&to_var=true"; 928 } 929 930 return file_path; 931 }, 932 933 /** 934 * Gets the path for the Display Options API 935 * 936 * @public 937 * 938 * @memberOf Cargo.API 939 * 940 * @return {string} 941 * @example Cargo.API.GetDisplayOptionsPath(); 942 */ 943 GetDisplayOptionsPath : function() { 944 var c = this.Config; // shorthand 945 var path = c.api_path + "/" + c.Version + "/displayoptions/" + c.cargo_url; 946 return path; 947 }, 948 949 /** 950 * Gets the path for the Images API 951 * 952 * @public 953 * 954 * @memberOf Cargo.API 955 * 956 * @param {number} [offset] Offset for pagination 957 * @param {number} [limit] Limit for pagination 958 * @return {string} 959 * @example Cargo.API.GetImagesPath(); 960 */ 961 GetImagesPath : function(offset, limit, sort) { 962 offset = (offset) ? offset : this.Config.current_offset; 963 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 964 sort = (sort) ? sort : 'newest'; 965 966 var path = this.GetSitePath() + "/images"; 967 path += "?offset=" + offset + "&limit=" + limit + "&sort=" + sort; 968 969 return path; 970 }, 971 972 /** 973 * Gets the path for the Projects API 974 * 975 * @public 976 * 977 * @memberOf Cargo.API 978 * 979 * @param {number} [offset] Offset for pagination 980 * @param {number} [limit] Limit for pagination 981 * @param {string} [type] Optional type param (page, project) 982 * @param {string} [filter] Optional filter name 983 * @param {string} [set] Optional set name 984 * @param {string} [route] Optional route name 985 * @return {string} 986 * @example Cargo.API.GetProjectsPath(offset, limit, type, filter, set, route); 987 */ 988 GetProjectsPath : function(offset, limit, type, filter, set, route) { 989 /* If this is a solo link, load single project */ 990 if( Cargo.Helper.IsSololink() ) { 991 return this.GetSingleProjectPath( Cargo.Model.Project.GetProjectUrl() ); 992 } 993 994 offset = (offset) ? offset : this.Config.current_offset; 995 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 996 type = (type) ? type : 'project'; 997 filter = (filter) ? filter : this.Config.filter; 998 set = (set) ? set : this.Config.set; 999 route = (route) ? route : Cargo.Helper.GetRouteName(); 1000 1001 // Cargo.Core.log(Cargo.Helper.GetCurrentRoute()+" -- "+Cargo.Helper.GetRouteName()); 1002 1003 var path = this.GetSitePath() + "/projects?type=" + type; 1004 path += "&offset=" + offset + "&limit=" + limit; 1005 1006 if( filter != "" && type != "page") { 1007 path += "&filter=" + encodeURIComponent(filter); 1008 } 1009 1010 if( set && type != "page" ) { 1011 path += "&set=" + encodeURIComponent(set); 1012 } 1013 1014 // if( route ) { 1015 // path += "&route=" + encodeURIComponent(route); 1016 // } 1017 1018 if( this.Config.site_recently_created ) { 1019 path += "&site_recently_created=true"; 1020 } 1021 1022 return path; 1023 }, 1024 1025 /** 1026 * Gets the path for the Admin Project List API 1027 * 1028 * @public 1029 * 1030 * @memberOf Cargo.API 1031 * 1032 * @param {number} [offset] Offset for pagination 1033 * @param {number} [limit] Limit for pagination 1034 * @return {string} 1035 * @example Cargo.API.GetAdminListPath(offset, limit); 1036 */ 1037 GetAdminListPath : function(offset, limit) { 1038 offset = (offset) ? offset : this.Config.current_offset; 1039 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1040 1041 var path = this.GetSitePath() + "/admincollection"; 1042 // path += "?offset=" + offset + "&limit=" + limit; 1043 1044 return path; 1045 }, 1046 1047 /** 1048 * Gets the path for the Following Rail 1049 * 1050 * @public 1051 * 1052 * @memberOf Cargo.API 1053 * 1054 * @return {string} 1055 * @example Cargo.API.GetRailPath(); 1056 */ 1057 GetRailPath : function() { 1058 return '/dispatch/rail/getRail/'+this.Config.cargo_url; 1059 }, 1060 1061 /** 1062 * Gets the path for the Site Search API 1063 * 1064 * @public 1065 * 1066 * @memberOf Cargo.API 1067 * 1068 * @param {string} [term] Search term 1069 * @param {number} [offset] Offset for pagination 1070 * @param {number} [limit] Limit for pagination 1071 * @param {string} [sort] The sort type ("relevant" || "recent" || "mostviewed") 1072 * @return {string} 1073 * @example Cargo.API.GetSearchPath(); 1074 */ 1075 GetSearchPath : function(term, offset, limit, sort) { 1076 term = (term) ? term : this.Config.search_term; 1077 offset = (offset) ? offset : this.Config.current_offset; 1078 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1079 sort = (sort) ? sort : 'relevant'; // 1080 1081 var path = this.GetSitePath() + "/search"; 1082 path += "?term=" + term + "&offset=" + offset + "&limit=" + limit + "&sort=" + sort; 1083 1084 return path; 1085 }, 1086 1087 /** 1088 * Gets the path for a single Project API 1089 * 1090 * @public 1091 * 1092 * @memberOf Cargo.API 1093 * @param {string} [purl] The Project URL 1094 * @param {string} [filter] Optional filter name 1095 * @param {string} [set] Optional set name 1096 * @return {string} 1097 * @example Cargo.API.GetSingleProjectPath(purl, filter, set); 1098 */ 1099 GetSingleProjectPath : function(purl, filter, set) { 1100 var c = this.Config; // shorthand 1101 var path = c.api_path + "/" + c.Version + "/projects/"; 1102 filter = (filter) ? filter : this.Config.filter; 1103 set = (set) ? set : this.Config.set; 1104 1105 if(typeof purl != "undefined" ) path += purl; 1106 path += "?url=" + c.cargo_url; 1107 1108 if( filter != "" ) { 1109 path += "&filter=" + encodeURIComponent(filter); 1110 } 1111 1112 if( set ) { 1113 path += "&set=" + encodeURIComponent(set); 1114 } 1115 1116 return path; 1117 }, 1118 1119 /** 1120 * Cargo navigation uses Display Options as it’s guide.
1121 * It will pull all pages and links outside of sets and filters. 1122 * This is the main style of navigation data for every website. 1123 * 1124 * @public 1125 * 1126 * @memberOf Cargo.API 1127 * @param {number} [offset] Offset for pagination 1128 * @param {number} [limit] Limit for pagination 1129 * @param {string} [set] Optional set name 1130 * @param {string} [filter] Optional filter name 1131 * @return {string} 1132 * @example Cargo.API.GetNavigationCargoPath(offset, limit, set); 1133 */ 1134 GetNavigationCargoPath : function(offset, limit, set, filter) { 1135 /* If this is a solo link, load single project */ 1136 if( Cargo.Helper.IsSololink() ) { 1137 return this.GetSingleProjectPath( Cargo.Model.Project.GetProjectUrl() ); 1138 } 1139 1140 offset = (offset) ? offset : this.Config.current_offset; 1141 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1142 set = (set) ? set : this.Config.set; 1143 filter = (filter) ? filter : Cargo.Helper.GetActiveFilter(); 1144 1145 var path = this.GetSitePath() + "/navigation"; 1146 path += "?offset=" + offset + "&limit=" + limit; 1147 1148 if( set ) { 1149 path += "&set=" + encodeURIComponent(set); 1150 } 1151 1152 if( filter ) { 1153 path += "&filter=" + encodeURIComponent(filter); 1154 } 1155 1156 return path; 1157 }, 1158 1159 /** 1160 * This method grabs only the exact navigation asked for. 1161 * This is used when only the data inside of a single set, 1162 * or single filter are required. 1163 * 1164 * @public 1165 * 1166 * @memberOf Cargo.API 1167 * @param {number} [offset] Offset for pagination 1168 * @param {number} [limit] Limit for pagination 1169 * @param {string} [set] Optional set name 1170 * @return {string} 1171 * @example Cargo.API.GetNavigationOnlyPath(offset, limit, set); 1172 */ 1173 GetNavigationOnlyPath : function(offset, limit, set) { 1174 /* If this is a solo link, load single project */ 1175 if( Cargo.Helper.IsSololink() ) { 1176 return this.GetSingleProjectPath( Cargo.Model.Project.GetProjectUrl() ); 1177 } 1178 1179 offset = (offset) ? offset : this.Config.current_offset; 1180 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1181 set = (set) ? set : this.Config.set; 1182 1183 var path = this.GetSitePath() + "/only_navigation"; 1184 path += "?offset=" + offset + "&limit=" + limit; 1185 1186 if( set ) { 1187 path += "&set=" + encodeURIComponent(set); 1188 } 1189 1190 return path; 1191 }, 1192 1193 /** 1194 * This method grabs all set data 1195 * 1196 * @public 1197 * 1198 * @memberOf Cargo.API 1199 * @param {number} [offset] Offset for pagination 1200 * @param {number} [limit] Limit for pagination 1201 * @return {string} 1202 * @example Cargo.API.GetSetsPath(offset, limit); 1203 */ 1204 GetSetsPath : function(offset, limit) { 1205 offset = (offset) ? offset : this.Config.current_offset; 1206 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1207 1208 var path = this.GetSitePath() + "/sets"; 1209 path += "?offset=" + offset + "&limit=" + limit; 1210 1211 return path; 1212 }, 1213 1214 /** 1215 * This method gets the location of the file put path 1216 * 1217 * @public 1218 * 1219 * @memberOf Cargo.API 1220 * @return {string} 1221 * @example Cargo.API.GetSiteFilesPutPath(); 1222 */ 1223 GetSiteFilesPutPath : function() { 1224 return '/dispatch/files/putFile'; 1225 }, 1226 1227 /* 1228 * ###################################################################### 1229 * 1230 * MODELS AND DATA 1231 * 1232 * ###################################################################### 1233 */ 1234 1235 /** 1236 * Helper method to create a data package with the model. 1237 * This binds the Display Options and Site model with the 1238 * incoming data 1239 * This is used when rendering Handlebars 1240 * 1241 * @public 1242 * 1243 * @memberOf Cargo.API 1244 * @param {string} [model_name] The name of the model 1245 * @param {JSON} [model] The raw data to be added to the model 1246 * @return {array} 1247 * @example Cargo.API.GetDataPackage(model_name, model); 1248 */ 1249 GetDataPackage : function(model_name, model) { 1250 var data_package = { 1251 "site" : Cargo.Model.Site.toJSON(), 1252 "display_options" : Cargo.Model.DisplayOptions.toJSON(), 1253 "config" : this.Config 1254 } 1255 1256 data_package[model_name] = model; 1257 1258 return data_package; 1259 }, 1260 1261 /* 1262 * ###################################################################### 1263 * 1264 * PROJECTS 1265 * 1266 * ###################################################################### 1267 */ 1268 1269 1270 /** 1271 * This will fetch a project collection via ajax by re-init a Backbone Collection 1272 * When we re-init, we set the value of the URL to fetch from. 1273 * After the fetch, the Views will update automatically 1274 * 1275 * @public 1276 * 1277 * @memberOf Cargo.API 1278 * @param {number} [offset] Offset for pagination 1279 * @param {number} [limit] Limit for pagination 1280 * @example Cargo.API.FetchProjectCollection(offset, limit); 1281 */ 1282 FetchProjectCollection : function(offset, limit) { 1283 offset = (offset) ? offset : this.Config.current_offset; 1284 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1285 1286 Cargo.Collection.Projects.url = Cargo.API.GetProjectsPath( offset, limit ); 1287 1288 var total_count = Cargo.Helper.GetTotalProjectsInMemory(); 1289 1290 if( Cargo.Helper.IsAutopaginate() ) { 1291 /* Auto pagination just adds to the collection */ 1292 Cargo.Collection.Projects.fetch({ 1293 remove: false, 1294 success: function(collection) { 1295 // Check to see if there is anything left to load, stop additional loads 1296 if((Math.floor(limit) + Math.floor(total_count)) >= Cargo.Helper.GetTotalProjectsInMemory()) { 1297 Cargo.View.Autopaginate.Data.is_updating = false;
1298 } 1299 Cargo.Event.trigger("pagination_complete", Cargo.API.Config.current_page ); 1300 } 1301 }); 1302 1303 } else { 1304 /* Normal pagination replaces the entire collection */ 1305 Cargo.Collection.Projects.fetch({reset: true}); 1306 } 1307 1308 }, 1309 1310 /** 1311 * This will fetch a Project Collection via ajax by re-init a Backbone Collection 1312 * When we re-init, we set the value of the URL to fetch from. 1313 * After the fetch, the Views will update automatically 1314 * 1315 * @public 1316 * 1317 * @memberOf Cargo.API 1318 * @param {string} [purl] Project URL 1319 * @param {object} [colletion] Optional collection 1320 * @example Cargo.API.GetSingleProject(purl, collection); 1321 */ 1322 GetSingleProject : function(purl, collection) { 1323 Cargo.Event.trigger("project_load_start", purl); 1324 1325 if(typeof collection == "undefined") { 1326 collection = Cargo.Collection.Projects; 1327 } 1328 1329 /* 1330 * Try and get the model from memory first 1331 */ 1332 var project_model = collection.findWhere( { project_url : purl } ); 1333 var page_model = Cargo.Collection.Pages.findWhere( { project_url : purl } ); 1334 var model = (page_model) ? page_model : project_model; 1335 1336 if( model && !this.Config.site_recently_created ) { 1337 /* 1338 * This model seems legit 1339 */ 1340 Cargo.Model.Project.set(model.toJSON()); 1341 1342 } else { 1343 Cargo.Core.log("No model found, async"); 1344 /* 1345 * If there is no model, then load one async 1346 */ 1347 Cargo.Model.Project.fetch( { data : { purl : purl }} ); 1348 1349 } 1350 1351 }, 1352 1353 /** 1354 * Gets a random project by querying the API 1355 */ 1356 GetRandomProject : function() { 1357 Cargo.Model.Project.fetch({ 1358 data : { 1359 url : Cargo.API.Config.cargo_url, 1360 random : true 1361 }, 1362 reset: false, 1363 success: function(model) { 1364 /* Navigate to the new URL, but do not trigger */ 1365 Cargo.Router.navigate( Cargo.Model.Project.GetProjectUrl(), { trigger: false }); 1366 } 1367 }); 1368 }, 1369 1370 /** 1371 * Sets a Project Page Collection 1372 * This will follow this order 1373 * - Save the current collection into a page memory 1374 * - Attempt to load the collection from memory 1375 * - If fail, then ajax load the project collection 1376 * - Set the new page and offset 1377 * 1378 * @private 1379 * 1380 * @memberOf Cargo.API 1381 * @param {number} [page] Page number changing to 1382 * @param {number} [current_page] Current page number 1383 * @example Cargo.API.SetProjectsPage(page, current_page); 1384 */ 1385 SetProjectsPage : function(page, current_page) { 1386 /* Get the offset and the current page */ 1387 var offset = Cargo.Helper.GetOffsetFromPage( page ); 1388 1389 /* Set the config values with the new values */ 1390 this.Config.current_page = page; 1391 this.Config.current_offset = offset; 1392 1393 /* Verify that we are not loading past our max, or pre our min */ 1394 if( offset <= Cargo.Model.DisplayOptions.GetTotalProjects() && offset >= 0 ) { 1395 /* Put the current collection into memory */ 1396 Cargo.Collection.Page[current_page] = Cargo.Collection.Projects.toJSON(); 1397 1398 /* Check for the new page in memory */ 1399 if( Cargo.Collection.Page[page] ) { 1400 /* This is in memory, so re-load the project with the stored data */ 1401 Cargo.Collection.Projects.reset( Cargo.Collection.Page[page] ); 1402 1403 } else { 1404 /* Fetch a collection based on the offset */ 1405 this.FetchProjectCollection( offset ); 1406 } 1407 1408 } 1409 1410 }, 1411 1412 1413 1414 1415 /* 1416 * ###################################################################### 1417 * 1418 * NAVIGATION 1419 * 1420 * ###################################################################### 1421 */ 1422 1423 /** 1424 * Returns the URL for the following link 1425 * 1426 * @public 1427 * 1428 * @memberOf Cargo.API 1429 * 1430 * @return {string} 1431 * @example Cargo.API.GetFollowingUrl(); 1432 */ 1433 GetFollowingUrl : function() { 1434 return Cargo.Model.Site.GetDirectLink() + "/following"; 1435 }, 1436 1437 /** 1438 * This will fetch a Project Collection via AJAX 1439 * by re-init a Backbone Collection 1440 * When we re-init, we set the value of the URL to fetch from 1441 * After the fetch, the views will update automatically 1442 * 1443 * @public 1444 * 1445 * @memberOf Cargo.API 1446 * @param {number} [offset] Offset for pagination 1447 * @param {number} [limit] Limit for pagination 1448 * @example Cargo.API.FetchNavigationCollection(offset, limit); 1449 */ 1450 FetchNavigationCollection : function(offset, limit) { 1451 offset = (offset) ? offset : this.Config.current_offset; 1452 limit = (limit) ? limit : Cargo.Helper.GetPageLimit(); 1453 1454 Cargo.Collection.Navigation.url = Cargo.API.GetNavigationCargoPath( offset, limit ); 1455 1456 if( Cargo.Helper.IsAutopaginate() ) { 1457 /* Auto pagination just adds to the collection */ 1458 Cargo.Collection.Navigation.fetch({remove: false}); 1459 1460 } else { 1461 /* Normal pagination replaces the entire collection */ 1462 Cargo.Collection.Navigation.fetch({reset: true}); 1463 } 1464 1465 }, 1466 1467 /** 1468 * Sets the next page of navigation 1469 * This will follow this order 1470 * - Save the current collection into a page memory 1471 * - Attempt to load the collection from memory 1472 * - If fail, then ajax load the navigation collection 1473 * - Set the new page and offset 1474 * 1475 * @private 1476 * 1477 * @memberOf Cargo.API 1478 * @param {number} [page] Page number changing to 1479 * @param {number} [current_page] Current page number 1480 * @example Cargo.API.SetNavigationPage(page, current_page); 1481 */ 1482 SetNavigationPage : function(page, current_page) { 1483 /* Get the offset and the current page */ 1484 var offset = Cargo.Helper.GetOffsetFromPage( page ); 1485 1486 /* Verify that we are not loading past our max, or pre our min */ 1487 if( offset <= Cargo.Model.DisplayOptions.GetTotalProjects() && offset >= 0 ) { 1488 /* Put the current collection into memory */ 1489 Cargo.Collection.NavPage[current_page] = Cargo.Collection.Navigation.toJSON(); 1490 1491 /* Check for the new page in memory */ 1492 if( Cargo.Collection.NavPage[page] ) { 1493 /* This is in memory, so re-load the project with the stored data */ 1494 Cargo.Collection.Navigation.reset( Cargo.Collection.NavPage[page] ); 1495 1496 } else { 1497 /* Fetch a collection based on the offset */ 1498 this.FetchNavigationCollection( offset ); 1499 } 1500 } 1501 1502 }, 1503 1504 /* 1505 * ###################################################################### 1506 * 1507 * SITE FILES 1508 * 1509 * ###################################################################### 1510 */ 1511 1512 /** 1513 * Save a file to site files 1514 * This method is using ajax for now 1515 * 1516 * @private 1517 * 1518 * @memberOf Cargo.API 1519 * @param {string} [file] Name of the file to save 1520 * @param {string} [data] data of the file to save 1521 * @param {function} [callback] A callback function 1522 * @example Cargo.API.PutSiteFile(file, data, function(data){ console.log(data); }); 1523 */ 1524 PutSiteFile : function(file, data, callback) { 1525 $.ajax({ 1526 type: 'POST', 1527 url: this.GetSiteFilesPutPath(), 1528 context: this, 1529 data: { 1530 file: file, 1531 data: data, 1532 uid: Cargo.Model.Site.GetId() 1533 }, 1534 dataType: 'json', 1535 success: function(data) { 1536 if (typeof callback === "function") { 1537 callback.call(this, data); 1538 } 1539 }, 1540 error: function(e) { 1541 1542 } 1543 }); 1544 } 1545 1546}); 1547 1548 1549 1550 /*************************************************************************** 1551 * 1552 * Action namespace 1553 * This is a shorthand wrapper for view actions and is intended 1554 * to make it easier and more accessible to call a simple action 1555 * 1556 **************************************************************************/ 1557 1558Action = { 1559 /*************************************************************************** 1560 * 1561 * Project actions 1562 * Usage: 1563 * Action.Project.Close(); 1564 * 1565 **************************************************************************/ 1566 Project : { 1567 1568 Index : function() { 1569 Cargo.View.Main.ShowIndex(); 1570 }, 1571 1572 Close : function() { 1573 Cargo.View.Main.ShowIndex(); 1574 }, 1575 1576 Next : function() { 1577 Cargo.View.Main.NextProject(); 1578 }, 1579 1580 Prev : function() { 1581 Cargo.View.Main.PrevProject(); 1582 }, 1583 1584 Random : function() { 1585 Cargo.View.Main.RandomProject(); 1586 } 1587 1588 }, 1589 1590 /*************************************************************************** 1591 * 1592 * Navigation actions 1593 * Usage 1594 * Action.Navigation.NextPage(); 1595 * 1596 **************************************************************************/ 1597 Navigation : { 1598 1599 NextPage : function() { 1600 Cargo.View.Main.NextPage(); 1601 }, 1602 1603 PrevPage : function() { 1604 Cargo.View.Main.PrevPage(); 1605 } 1606 } 1607 1608} 1609 1610/*************************************************************************** 1611 * 1612 * Events 1613 * This is the events repository 1614 * Create events for state change by extending backbone 1615 * DOM manipulation 1616 * This file is included in the cargo/init 1617 * 1618 **************************************************************************/ 1619 1620/*************************************************************************** 1621 * 1622 * Backbone Events 1623 * 1624 **************************************************************************/ 1625 1626 /** 1627 * Example of binding to an event: 1628 * <pre class="prettyprint"> 1629 * Cargo.Event.on("project_load_complete", function( pid ) { 1630 * alert("Project " + pid + " loaded!"); 1631 * }); 1632 * </pre> 1633 * Example of triggering an event: 1634 * <pre class="prettyprint"> 1635 * Cargo.Event.trigger("add_history", "/Project-title"); 1636 * </pre> 1637 * 1638 * @namespace Events 1639 * @memberOf Cargo 1640 * @extends {Backbone.Events} 1641 */ 1642 1643 /* 1644 * The history object is created in bootstrap 1645 */ 1646 _.extend(Cargo.Event, Backbone.Events); 1647 1648 /** 1649 * Adding history 1650 * Simply push a single URL onto the history stack 1651 * by navigating to that URL 1652 * 1653 * @event add_history 1654 * @memberOf Cargo.Events 1655 * 1656 * @param {string} [url] URL to be added to history 1657 * @param {string} [id] ID of the project (if there is one) 1658 * 1659 * @example Cargo.Event.on("add_history", function(url, id) { 1660 * // Methods after event is triggered 1661 *}); 1662 */ 1663 Cargo.Event.on("add_history", function( url, id ) { 1664 url = Cargo.Helper.NormalizeRouteLink(url); 1665 1666 // Whenever you are, we're already there 1667 if(url === Cargo.Helper.GetCurrentProjectUrl()){ 1668 if( !Cargo.Model.DisplayOptions.GetDisableProjectScroll() ) { 1669 Cargo.Helper.ScrollToTop(); 1670 } 1671 return; 1672 } 1673
1674 /* Show the loading animation */ 1675 if( !Cargo.Helper.IsCurrentRoute(url) ) { 1676 id = (id) ? id : Cargo.Helper.GetPidFromPurl(url); 1677 Cargo.Helper.ShowLoadingAnimation( id, url ); 1678 } else { 1679 /* Scroll to top */ 1680 if( !Cargo.Model.DisplayOptions.GetDisableProjectScroll() ) { 1681 Cargo.Helper.ScrollToTop(); 1682 } 1683 } 1684 1685 /* Navigate to the new URL */ 1686 Cargo.Router.navigate( url, { trigger: true }); 1687 1688 }); 1689 1690 /** 1691 * Adding history passively 1692 * Change the URL, but don't fire the event 1693 * 1694 * @event add_history_passive 1695 * @memberOf Cargo.Events 1696 * @private 1697 * 1698 * @param {string} [url] URL to be added to history 1699 * 1700 * @example Cargo.Event.on("add_history_passive", function(url) { 1701 * // Methods after event is triggered 1702 *}); 1703 */ 1704 Cargo.Event.on("add_history_passive", function( url ) { 1705 url = Cargo.Helper.NormalizeRouteLink(url); 1706 1707 /* Navigate to the new URL */ 1708 Cargo.Router.navigate( url, { trigger: false }); 1709 1710 }); 1711 1712 /** 1713 * Page load when editing a project 1714 * 1715 * @memberOf Cargo.Events 1716 * 1717 * @event admin_edit_load_complete 1718 * 1719 * @example Cargo.Event.on("admin_edit_load_complete", function() { 1720 * // Methods after event is triggered 1721 *}); 1722 */ 1723 Cargo.Event.on("admin_edit_load_complete", function( ) { 1724 1725 }); 1726 1727 /** 1728 * When the audio player is started / stopped 1729 * 1730 * @event audio_play_pause 1731 * @memberOf Cargo.Events 1732 * 1733 * @param {string} [rel] The rel attribute of the button clicked 1734 * @param {string}
1734 [which] Which action: play/pause 1735 * 1736 * @example Cargo.Event.on("audio_play_pause", function(rel, which) { 1737 * // Methods after event is triggered 1738 *}); 1739 */ 1740 Cargo.Event.on("audio_play_pause", function( rel, which ) { 1741 1742 }); 1743 1744 /** 1745 * Fires after the columnizer is done updating. 1746 * Note this can be triggered often and repeatedly. 1747 * 1748 * @event columnizer_update_complete 1749 * 1750 * @memberOf Cargo.Events 1751 * 1752 * @example Cargo.Event.on("columnizer_update_complete", function() { 1753 * // Methods after event is triggered 1754 *}); 1755 */ 1756 1757 Cargo.Event.on("columnizer_update_complete", function() { 1758 1759 }); 1760 1761 /** 1762 * When on a custom route 1763 * 1764 * @event custom_route_loaded 1765 * 1766 * @memberOf Cargo.Events 1767 * 1768 * @param {string} [location] The route location 1769 * 1770 * @example Cargo.Event.on("custom_route_loaded", function(location) { 1771 * // Methods after event is triggered 1772 *}); 1773 */ 1774 Cargo.Event.on("custom_route_loaded", function( location ) { 1775 1776 }); 1777 1778 /** 1779 * When on a direct link 1780 * 1781 * @event direct_link_loaded 1782 * 1783 * @memberOf Cargo.Events 1784 * 1785 * @param {string} [location] The link loaded 1786 * 1787 * @example Cargo.Event.on("direct_link_loaded", function(location) { 1788 * // Methods after event is triggered 1789 *}); 1790 */ 1791 Cargo.Event.on("direct_link_loaded", function( location ) { 1792 $(document).trigger("directLinkLoaded", [ location ]); 1793 }); 1794 1795 /** 1796 * When the first navigation collection reset event is triggered 1797 * 1798 * @event first_navigation_collection_reset 1799 * 1800 * @memberOf Cargo.Events 1801 * 1802 * @example Cargo.Event.on("first_navigation_collection_reset", function() { 1803 * // Methods after event is triggered 1804 *}); 1805 */ 1806 Cargo.Event.on("first_navigation_collection_reset", function( ) { 1807 1808 }); 1809 1810 /** 1811 * When the first project collection reset event is triggered 1812 * 1813 * @event first_project_collection_reset 1814 * 1815 * @memberOf Cargo.Events 1816 * 1817 * @example Cargo.Event.on("first_project_collection_reset", function() { 1818 * // Methods after event is triggered 1819 *}); 1820 */ 1821 Cargo.Event.on("first_project_collection_reset", function( ) { 1822 Cargo.API.Config.first_load_finished = true; 1823 }); 1824 1825 /** 1826 * When the fullscreen mode is closed 1827 * 1828 * @event fullscreen_close 1829 * 1830 * @memberOf Cargo.Events 1831 * 1832 * @example Cargo.Event.on("fullscreen_close", function() { 1833 * // Methods after event is triggered 1834 *}); 1835 */ 1836 Cargo.Event.on("fullscreen_close", function( ) { 1837 1838 }); 1839 1840 /** 1841 * When the fullscreen mode destroys the hotkeys 1842 * rebind any design keys 1843 * 1844 * @event fullscreen_destroy_hotkeys 1845 * 1846 * @memberOf Cargo.Events 1847 * 1848 * @example Cargo.Event.on("fullscreen_destroy_hotkeys", function() { 1849 * // Methods after event is triggered 1850 *}); 1851 */ 1852 Cargo.Event.on("fullscreen_destroy_hotkeys", function( ) { 1853 Cargo.Core.KeyboardShortcut.Add("Esc", 27, function() { 1854 $("a.follow_popup_cancel").click(); 1855 if ( ! $("#freshbox").length > 0 ) { 1856 Cargo.app.Rail.Events.trigger("rail:hide"); 1857 } 1858 }); 1859 }); 1860 1861 1862 /** 1863 * When the fullscreen mode is initialized 1864 * 1865 * @event fullscreen_open 1866 * 1867 * @memberOf Cargo.Events 1868 * 1869 * @example Cargo.Event.on("fullscreen_open", function() { 1870 * // Methods after event is triggered 1871 *}); 1872 */ 1873 Cargo.Event.on("fullscreen_open", function( ) { 1874 1875 }); 1876 1877 /** 1878 * When the homepage is loaded 1879 * 1880 * @event homepage_loaded 1881 * 1882 * @memberOf Cargo.Events 1883 * 1884 * @example Cargo.Event.on("homepage_loaded", function() { 1885 * // Methods after event is triggered 1886 *}); 1887 */ 1888 Cargo.Event.on("homepage_loaded", function() { 1889 if(Cargo.View.Navigation) { 1890 Cargo.View.Navigation.AddSetActive(); 1891 } 1892 1893 // Scroll the page back to the top 1894 if( !Cargo.Model.DisplayOptions.GetDisableProjectScroll() ) { 1895 Cargo.Helper.ScrollToTop(); 1896 } 1897 }); 1898 1899 /** 1900 * When the inspector is unloaded 1901 * 1902 * @event inspector_unload 1903 * 1904 * @memberOf Cargo.Events 1905 * 1906 * @example Cargo.Event.on("inspector_unload", function() { 1907 * // Methods after event is triggered 1908 *}); 1909 */ 1910 Cargo.Event.on("inspector_unload", function( ) { 1911 // Rebind all of the objects again 1912 Cargo.Collection.Projects.reset( Cargo.Collection.Projects.models );
1913 Cargo.Event.Common(); 1914 }); 1915 1916 /** 1917 * When a the loading animations have been replaced 1918 * 1919 * @event loading_anims_replaced 1920 * 1921 * @memberOf Cargo.Events 1922 * 1923 * @example Cargo.Event.on("loading_anims_replaced", function() { 1924 * // Methods after event is triggered 1925 *}); 1926 */ 1927 Cargo.Event.on("loading_anims_replaced", function( ) { 1928 1929 }); 1930 1931 /** 1932 * When the freshbox launches 1933 * 1934 * @event lightbox_open 1935 * 1936 * @memberOf Cargo.Events 1937 * 1938 * @example Cargo.Event.on("lightbox_open", function() { 1939 * // Methods after event is triggered 1940 *}); 1941 */ 1942 Cargo.Event.on("lightbox_open", function( ) { 1943 1944 }); 1945 1946 /** 1947 * When the freshbox is all done 1948 * 1949 * @event lightbox_close 1950 * 1951 * @memberOf Cargo.Events 1952 * 1953 * @example Cargo.Event.on("lightbox_close", function() { 1954 * // Methods after event is triggered 1955 *}); 1956 */ 1957 Cargo.Event.on("lightbox_close", function( ) { 1958 1959 }); 1960 1961 /** 1962 * When the navigation content has been reset with a new model 1963 * 1964 * @event navigation_reset 1965 * 1966 * @memberOf Cargo.Events 1967 * 1968 * @example Cargo.Event.on("navigation_reset", function() { 1969 * // Methods after event is triggered 1970 *}); 1971 */ 1972 Cargo.Event.on("navigation_reset", function() { 1973 1974 }); 1975 1976 /** 1977 * When the lightbox has been created and is ready in the window 1978 * Triggered by proxying photoswipe events 1979 * 1980 * @event lightbox-init 1981 * 1982 * @param {object} [lightbox] The photoswipe instance 1983 * 1984 * @memberOf Cargo.Events 1985 * 1986 * @example Cargo.Event.on("lightbox-init", function() { 1987 * // Methods after event is triggered 1988 *}); 1989 */ 1990 Cargo.Event.on('lightbox-init', function(lightbox){ 1991 1992 // hack to prevent lightbox from janking on iOS's slow resize 1993 if( Cargo.Helper.isMobile()) { 1994 var resizeTimeout 1995 $(window).on('resize.lightbox', function(){ 1996 clearTimeout(resizeTimeout); 1997 resizeTimeout = setTimeout(function(){ 1998 lightbox.updateSize(true); 1999 }, 500); 2000 }) 2001 } 2002 }); 2003 2004 /** 2005 * When the lightbox has been destroyed 2006 * Triggered by proxying photoswipe events 2007 * 2008 * @event lightbox-destroy 2009 * 2010 * @param {object} [lightbox] The photoswipe instance 2011 * 2012 * @memberOf Cargo.Events 2013 * 2014 * @example Cargo.Event.on("lightbox-init", function() { 2015 * // Methods after event is triggered 2016 *}); 2017 */ 2018 Cargo.Event.on('lightbox-destroy', function(lightbox){ 2019 if( Cargo.Helper.isMobile()) { 2020 $(window).off('resize.lightbox'); 2021 } 2022 }); 2023 2024 /** 2025 * When a set in the navigation is being opened or closed 2026 * 2027 * @event navigation_set_toggle 2028 * 2029 * @memberOf Cargo.Events 2030 * 2031 * @example Cargo.Event.on("navigation_set_toggle", function() { 2032 * // Methods after event is triggered 2033 *}); 2034 */ 2035 Cargo.Event.on("navigation_set_toggle", function() { 2036 2037 }); 2038 2039 /** 2040 * Go to the next page 2041 * 2042 * @event next_page 2043 * 2044 * @memberOf Cargo.Events 2045 * 2046 * @param {number} [page] Page number 2047 * 2048 * @example Cargo.Event.on("next_page", function(page) { 2049 * // Methods after event is triggered 2050 *}); 2051 */ 2052 Cargo.Event.on("next_page", function( new_page ) { 2053 $(document).trigger("pageChange", [new_page]); 2054 }); 2055 2056 /** 2057 * Go to the next project 2058 * 2059 * @event next_project 2060 * 2061 * @memberOf Cargo.Events 2062 * 2063 * @param {number} [pid] PID of the project to be loaded 2064 * 2065 * @example Cargo.Event.on("next_project", function(pid) { 2066 * // Methods after event is triggered 2067 *}); 2068 */ 2069 Cargo.Event.on("next_project", function( pid ) { 2070 $(document).trigger("showNextProject", [pid]); 2071 }); 2072 2073 /** 2074 * When the pagination is done 2075 * 2076 * @event pagination_complete 2077 * 2078 * @memberOf Cargo.Events 2079 * 2080 * @param {number} [page] Page number of the newly loaded page 2081 * 2082 * @example Cargo.Event.on("pagination_complete", function(page) { 2083 * // Methods after event is triggered 2084 *}); 2085 */ 2086 Cargo.Event.on("pagination_complete", function( new_page ) { 2087 $(document).trigger("pageChangeComplete", [new_page] ); 2088 $(document).trigger("paginationComplete", [new_page] ); 2089 Cargo.Helper.HideLoadingAnimation(); 2090 2091 // Common events that are fired multiple times 2092 Cargo.Event.Common(); 2093 2094 /* Add an active state to the navigation */ 2095 if(Cargo.View.Navigation) { 2096 Cargo.View.Navigation.AddActive(); 2097 Cargo.View.Navigation.AddSetActive(); 2098 } 2099 2100 /* Add an active state to the thumbnail */ 2101 if(Cargo.View.Thumbnail) { 2102 Cargo.View.Thumbnail.AddActive(); 2103 } 2104 2105 }); 2106 2107 /** 2108 * When the pagination starts 2109 * 2110 * @event pagination_start 2111 * 2112 * @memberOf Cargo.Events 2113 * 2114 * @param {number} [page] Page number of the page to be loaded 2115 * 2116 * @example Cargo.Event.on("pagination_start", function(page) { 2117 * // Methods after event is triggered 2118 *}); 2119 */ 2120 Cargo.Event.on("pagination_start", function( new_page ) { 2121 Cargo.Helper.ShowLoadingAnimation(); 2122 2123 }); 2124 2125 /** 2126 * Go to the previous page 2127 * 2128 * @event prev_page 2129 * 2130 * @memberOf Cargo.Events 2131 * 2132 * @param {number} [page] Page number to go back to 2133 * 2134 * @example Cargo.Event.on("prev_page", function(page) { 2135 * // Methods after event is triggered 2136 *}); 2137 */ 2138 Cargo.Event.on("prev_page", function( new_page ) { 2139 $(document).trigger("pageChange", [new_page]); 2140 }); 2141 2142 /** 2143 * Go to the previous project 2144 * 2145 * @event prev_project 2146 * 2147 * @memberOf Cargo.Events 2148 * 2149 * @param {number} [pid] PID of the previous project 2150 * 2151 * @example Cargo.Event.on("prev_project", function(pid) { 2152 * // Methods after event is triggered 2153 *}); 2154 */ 2155 Cargo.Event.on("prev_project", function( pid ) { 2156 $(document).trigger("showPrevProject", [pid]); 2157 }); 2158 2159 /** 2160 * Fires whenever a project is closing, either to the index or another page/project 2161 * 2162 * @event project_change 2163 * 2164 * @memberOf Cargo.Events 2165 * 2166 * @example Cargo.Event.on("project_change", function() { 2167 * // Methods after event is triggered 2168 *}); 2169 */ 2170 Cargo.Event.on("project_change", function() { 2171 2172 }); 2173 2174 /** 2175 * When a the project collection has loaded 2176 * 2177 * @event project_collection_reset 2178 * 2179 * @memberOf Cargo.Events 2180 * 2181 * @example Cargo.Event.on("project_collection_reset", function() { 2182 * // Methods after event is triggered 2183 *}); 2184 */ 2185 Cargo.Event.on("project_collection_reset", function( ) { 2186 2187 }); 2188 2189 /** 2190 * When the a project is finished loading 2191 * 2192 * @event project_load_complete 2193 * 2194 * @memberOf Cargo.Events 2195 * 2196 * @param {number} [pid] PID of the project to be loaded 2197 * 2198 * @example Cargo.Event.on("project_load_complete", function(pid) { 2199 * // Methods after event is triggered 2200 *}); 2201 */ 2202 Cargo.Event.on("project_load_complete", function( pid ) { 2203 $(document).trigger("projectLoadComplete", [ pid ]); 2204 2205 /* Reset the Project Transition */ 2206 Cargo.Event.ProjectTransition = new $.Deferred(); 2207 2208 /* Common events that are fired multiple times */ 2209 Cargo.Event.Common(); 2210 2211 /* Scroll the page back to the top */ 2212 if( !Cargo.Model.DisplayOptions.GetDisableProjectScroll() ) { 2213 Cargo.Helper.ScrollToTop(); 2214 } 2215 2216 /* Change the bg color, if needed */ 2217 if(Cargo.View.ProjectDetail) { 2218 Cargo.View.ProjectDetail.ChangeBgColorFromModel(); 2219 } 2220 2221 /* Remove the set class from body */ 2222 if(Cargo.View.Main && !Cargo.Helper.IsOnStartProject()) { 2223 Cargo.View.Main.RemoveStartProjectClass(); 2224 } 2225 2226 2227 /* Add an active state to the navigation */ 2228 if(Cargo.View.Navigation) { 2229 Cargo.View.Navigation.AddActive(pid); 2230 Cargo.View.Navigation.AddSetActive(); 2231 } 2232 2233 /* Add an active state to the thumbnail */ 2234 if(Cargo.View.Thumbnail) { 2235 Cargo.View.Thumbnail.AddActive(pid); 2236 Cargo.View.Thumbnail.CheckAndHideThumbs(); 2237 } 2238 2239 /* Hide the loading animations */ 2240 Cargo.Helper.HideLoadingAnimation(); 2241 2242 /* Set the html page title */ 2243 if( !Cargo.Helper.IsOnStartProject() ) { 2244 Cargo.View.Main.SetWindowTitle(); 2245 } 2246
2247 /* Set the data-type on the body */ 2248 Cargo.View.Main.SetDataType(Cargo.Model.Project.GetType()); 2249 2250 // Gracefully track project load as a pageview 2251 if (! Cargo.API.Config.is_direct_link && ! Cargo.Helper.IsOnStartProject()) { 2252 Cargo.Analytics.pageview(true); 2253 } 2254 }); 2255 2256 /** 2257 * When a project starts to load 2258 * 2259 * @event project_load_start 2260 * 2261 * @memberOf Cargo.Events 2262 * 2263 * @param {string} [purl] Project URL about to be loaded 2264 * 2265 * @example Cargo.Event.on("project_load_start", function(purl) { 2266 * // Methods after event is triggered 2267 *}); 2268 */ 2269 Cargo.Event.on("project_load_start", function( purl ) { 2270 Cargo.Event.trigger('project_change'); 2271 $(document).trigger("startProjectLoaded", [purl]); 2272 2273 // Set is_direct_link to false once we start loading our projects through JS 2274 Cargo.API.Config.is_direct_link = false; 2275 }); 2276 2277 /** 2278 * Go to a random project 2279 * 2280 * @event random_project 2281 * 2282 * @memberOf Cargo.Events 2283 * 2284 * @param {number} [pid] PID of a random project to be loaded 2285 * 2286 * @example Cargo.Event.on("random_project", function(pid) { 2287 * // Methods after event is triggered 2288 *}); 2289 */ 2290 Cargo.Event.on("random_project", function( pid ) { 2291 $(document).trigger("showRandomProject", [pid]); 2292 }); 2293 2294 /** 2295 * Triggered once the navigation reseed is complete 2296 * Reseeding happens when the admin refreshes the front end via frame 2297 * 2298 * @event reseed_navigation_complete 2299 * 2300 * @memberOf Cargo.Events 2301 * 2302 * 2303 * @example Cargo.Event.on("reseed_navigation_complete", function() { 2304 * // Methods after event is triggered 2305 *}); 2306 */ 2307 Cargo.Event.on("reseed_navigation_complete", function( pid ) { 2308 2309 }); 2310 2311 /** 2312 * Triggered once the project reseed is complete 2313 * Reseeding happens when the admin refreshes the front end via frame 2314 * 2315 * @event reseed_project_complete 2316 * 2317 * @memberOf Cargo.Events 2318 * 2319 * 2320 * @example Cargo.Event.on("reseed_project_complete", function() { 2321 * // Methods after event is triggered 2322 *}); 2323 */ 2324 Cargo.Event.on("reseed_project_complete", function( pid ) { 2325 2326 }); 2327 2328 /**
2329 * When on a set 2330 * 2331 * @event set_loaded 2332 * 2333 * @memberOf Cargo.Events 2334 * 2335 * @param {string} [location] The URL of the set 2336 * 2337 * @example Cargo.Event.on("set_loaded", function(location) { 2338 * // Methods after event is triggered 2339 *}); 2340 */ 2341 Cargo.Event.on("set_loaded", function( location ) { 2342 2343 }); 2344 2345 /** 2346 * When a the index is completely loaded (project is closed) 2347 * 2348 * @event show_index_complete 2349 * 2350 * @memberOf Cargo.Events 2351 * 2352 * @param {number} [pid] PID of the project recently closed 2353 * 2354 * @example Cargo.Event.on("show_index_complete", function(pid) { 2355 * // Methods after event is triggered 2356 *}); 2357 */ 2358 Cargo.Event.on("show_index_complete", function( pid ) { 2359 $(document).trigger("projectCloseComplete", [pid]); 2360 $(document).trigger("projectIndex"); 2361 2362 /* Remove active state to the navigation */ 2363 if(Cargo.View.Navigation) { 2364 Cargo.View.Navigation.RemoveActive(); 2365 } 2366 2367 /* Remove active state to the thumbnails */ 2368 if(Cargo.View.Thumbnail) { 2369 Cargo.View.Thumbnail.RemoveActive(); 2370 Cargo.View.Thumbnail.ShowThumbs(); 2371 } 2372 2373 /* Set the html page title */ 2374 Cargo.View.Main.SetWindowTitle(true); 2375 2376 /* Remove the data-type on the body */ 2377 Cargo.View.Main.SetDataType(null); 2378 2379 /* Remove the start project class */ 2380 Cargo.View.Main.RemoveStartProjectClass(); 2381 2382 /* Reset the Project Transition */ 2383 Cargo.Event.IndexTransition = new $.Deferred(); 2384 }); 2385 2386 /** 2387 * When the index is about to be shown (project is about to close) 2388 *
2389 * @event show_index_start 2390 * 2391 * @memberOf Cargo.Events 2392 * 2393 * @param {number} [pid] PID of the project currently open 2394 * 2395 * @example Cargo.Event.on("show_index_start", function(pid) { 2396 * // Methods after event is triggered 2397 *}); 2398 */ 2399 Cargo.Event.on("show_index_start", function( pid ) { 2400 Cargo.Event.trigger('project_change') 2401 $(document).trigger("projectClose", [pid]); 2402 }); 2403 2404 /** 2405 * Slideshow is resizing 2406 * 2407 * @event slideshow_resize 2408 * 2409 * @memberOf Cargo.Events 2410 * 2411 * @param {object} [el] Slideshow Element 2412 * @param {object} [obj] The slideshow instance 2413 * 2414 * @example Cargo.Event.on("slideshow_resize", function(el, obj) { 2415 * // Methods after event is triggered 2416 *}); 2417 */ 2418 Cargo.Event.on("slideshow_resize", function( el, obj ) { 2419 2420 }); 2421 2422 /** 2423 * Slideshow transition is starting 2424 * 2425 * @event slideshow_transition_start 2426 * 2427 * @memberOf Cargo.Events 2428 * 2429 * @param {object} [el] Slideshow Element 2430 * @param {object} [obj] The slideshow instance 2431 * 2432 * @example Cargo.Event.on("slideshow_transition_start", function(el, obj) { 2433 * // Methods after event is triggered 2434 *}); 2435 */ 2436 Cargo.Event.on("slideshow_transition_start", function( el, obj ) { 2437 $(document).trigger("slideshowTransitionStart"); 2438 }); 2439 2440 /** 2441 * Slideshow transition is finished 2442 * 2443 * @event slideshow_transition_finish 2444 * 2445 * @memberOf Cargo.Events 2446 * 2447 * @param {object} [el] Slideshow Element 2448 * @param {object} [obj] The slideshow instance 2449 * 2450 * @example Cargo.Event.on("slideshow_transition_finish", function(el, obj) { 2451 * // Methods after event is triggered 2452 *}); 2453 */ 2454 Cargo.Event.on("slideshow_transition_finish", function( el, obj ) { 2455 $(document).trigger("slideshowTransitionFinish"); 2456 }); 2457 2458 /**
2459 * When on a solo link 2460 * 2461 * @event solo_link_loaded 2462 * 2463 * @memberOf Cargo.Events 2464 * 2465 * @param {string} [location] URL of the link loaded 2466 * 2467 * @example Cargo.Event.on("solo_link_loaded", function(location) { 2468 * // Methods after event is triggered 2469 *}); 2470 */ 2471 Cargo.Event.on("solo_link_loaded", function( location ) { 2472 2473 }); 2474 2475 /** 2476 * When a template has been loaded 2477 * 2478 * @event template_loaded 2479 * 2480 * @memberOf Cargo.Events 2481 * 2482 * @param {string} [template] Name of the template loaded 2483 * 2484 * @example Cargo.Event.on("template_loaded", function(template) { 2485 * // Methods after event is triggered 2486 *}); 2487 */ 2488 Cargo.Event.on("template_loaded", function( template_name ) { 2489 2490 }); 2491 2492 2493 /** 2494 * Common events that are fired regularly 2495 * 2496 * @method common 2497 * @private 2498 * 2499 * @memberOf Cargo.Events 2500 */ 2501 Cargo.Event.Common = function() { 2502 /* Check for, and activate, slideshows */ 2503 if(typeof Cargo.Core.Slideshow.InitSlideshow != "undefined") { 2504 Cargo.Core.Slideshow.InitSlideshow(); 2505 } 2506 2507 /* Check for, and activate, video fallback */ 2508 Cargo.Core.Video.CheckForVideo(); 2509 2510 /* Find an init any audio players */ 2511 Cargo.Core.Audio.CheckForAudio(); 2512 2513 /* Look for a search form in a project or on the page */ 2514 Cargo.Core.FindAndLoadSearch(); 2515 2516 /* Check and init freshbox, but only on non-permalink */ 2517 if( !Cargo.Helper.IsPermalink( Cargo.Model.Project.GetProjectUrl() ) ) { 2518 Cargo.Core.CheckAndInitFreshbox(); 2519 } 2520 2521 /* Remove the freshbox */ 2522 if( $("#freshbox").length > 0) { 2523 Cargo.View.Fullscreen.Close(); 2524 } 2525 2526 } 2527 2528/*************************************************************************** 2529 * 2530 * Project transition event 2531 * 2532 **************************************************************************/ 2533 /** 2534 * A global for holding a jquery Deferred object 2535 * This is used for project and index transitions 2536 * 2537 * @type {jQuery} 2538 * @method common 2539 * @private 2540 * 2541 * @memberOf Cargo.Events 2542 */ 2543 Cargo.Event.ProjectTransition = new $.Deferred(); 2544 Cargo.Event.IndexTransition = new $.Deferred(); 2545 2546 /** 2547 * A global method that is meant to be overwritten by designs 2548 * This is a method that will run while transitioning a project open 2549 * The project opening is deferred until this method resolves itself. 2550 * 2551 * @param {String} pid The project ID 2552 * @param {String} project_markup The rendered HTML for the requested project 2553 * @param {Object} project_model The full project model 2554 * @method common 2555 * @private 2556 * 2557 * @memberOf Cargo.Events 2558 */ 2559 Cargo.Event.ShowProjectTransition = function(pid, project_markup, project_model) { 2560 Cargo.Event.ProjectTransition.resolve(); 2561 return Cargo.Event.ProjectTransition.promise(); 2562 } 2563 2564 /** 2565 * A global method that is meant to be overwritten by designs 2566 * This is a method that will run while transitioning a project close / show index 2567 * The index showing is deferred until this method resolves itself. 2568 * 2569 * @param {String} pid The project ID being closed 2570 * @param {Object} project_model The full project model 2571 * @method common 2572 * @private 2573 * 2574 * @memberOf Cargo.Events 2575 */ 2576 Cargo.Event.ShowIndexTransition = function(pid, project_model) { 2577 Cargo.Event.IndexTransition.resolve(); 2578 return Cargo.Event.IndexTransition.promise(); 2579 } 2580 2581/*************************************************************************** 2582 * 2583 * jQuery Events 2584 * 2585 **************************************************************************/ 2586 2587 2588 /* 2589 * This is the close event from Cargo 1.0 2590 * When triggered, it will close a project 2591 * For now, this remains in this file. Probably should move it 2592 */ 2593 $(document).bind("closeProject", function(e) { 2594 Action.Project.Close(); 2595 }); 2596 2597 2598 2599/*************************************************************************** 2600 * 2601 * Helpers 2602 * This contains various helper methods that are used 2603 * in various views 2604 * 2605 **************************************************************************/ 2606 2607/** 2608 * The Cargo namespace holds all fields and methods 2609 * @namespace Cargo 2610 */ 2611 2612/** 2613 * @namespace o 2614 * @memberOf Cargo 2615 */ 2616 2617/** 2618 * @constructor Cargo.o.Helpers 2619 * @extends {Backbone.View} 2620 * @memberOf Cargo.o 2621 */ 2622 2623 2624 2625Cargo.o.helpers = Backbone.View.extend({ 2626 Data : { 2627 is_loading : false, 2628 loading_timout : 1000 2629 }, 2630 2631 /** 2632 * Checks to see if the browser allows fullscreen 2633 * 2634 * @public 2635 * 2636 * @memberOf Cargo.o.Helpers 2637 * 2638 * @return {boolean} True if fullscreen is supported, false if not. 2639 * @example Cargo.Helper.AllowsFullScreen(); 2640 */ 2641 AllowsFullScreen : function() { 2642 var docElem = document.documentElement; 2643 if (docElem.requestFullscreen || docElem.mozRequestFullScreen || docElem.msRequestFullscreen || docElem.webkitRequestFullScreen) { 2644 return true; 2645 } else { 2646 return false;
2647 } 2648 }, 2649 2650 /** 2651 * Find Project Collection based on Project URL 2652 * 2653 * @public 2654 * @memberOf Cargo.o.Helpers 2655 * 2656 * @param {String} [project_url] URL of the Project 2657 * 2658 * @return {object} Project collection 2659 * @example Cargo.Helper.FindCollectionByProjectUrl(url); 2660 */ 2661 FindCollectionByProjectUrl : function( project_url ) { 2662 var found_collection = false; 2663 _.each(Cargo.Collection.Page, function(collection) { 2664 if( !found_collection ) { 2665 var dummy_collection = new Backbone.Collection(); 2666 dummy_collection.reset(collection); 2667 var model = dummy_collection.where( { project_url : project_url } ); 2668 2669 if(model[0]) { 2670 found_collection = collection; 2671 } 2672 } 2673 }); 2674 2675 return found_collection; 2676 }, 2677 2678 /** 2679 * Get the active Filter 2680 * 2681 * @memberof Cargo.o.Helpers 2682 * 2683 * @return {string} Filter name 2684 * @example Cargo.Helper.GetActiveFilter(); 2685 */ 2686 GetActiveFilter : function() { 2687 return Cargo.API.Config.filter; 2688 }, 2689 2690 /** 2691 * Get the active index. This will return 2692 * /url/filter/cat OR /url/set OR /url 2693 * Whatever the current page's index is 2694 * 2695 * @memberof Cargo.o.Helpers 2696 * 2697 * @param {Boolean} [full_path] Returns the full http://domain.com/path or http://cargocollective.com/url/path 2698 * @return {string} Index path 2699 * @example Cargo.Helper.GetActiveIndex(); 2700 */ 2701 GetActiveIndex : function(full_path) { 2702 var relative_path = Cargo.Helper.GetHomeUrl(true, true); 2703 2704 return (full_path) ? window.location.protocol + '//'+this.GetBaseDomain()+relative_path : relative_path; 2705 }, 2706 2707 /** 2708 * This is a way to load backbone collections, models and views 2709 * without knowing the case. It is useful because of the data-view attributes 2710 * that do all the dynamic loading on each page 2711 * 2712 * @public 2713 * @memberof Cargo.o.Helpers 2714 * 2715 * @param {object} [obj] backbone object 2716 * @param {string} [model] Name of the requested model 2717 * 2718 * @return {string} Name actual name of the model 2719 * @example Cargo.Helper.GetBackboneCaseInsensitive(obj, model); 2720 */ 2721 GetBackboneCaseInsensitive : function(obj, requested_model) { 2722 var mapped_methods = {}; 2723 2724 for(var f in obj){ 2725 // console.log(f+" -- "+obj.hasOwnProperty(f)+" -- "+typeof obj[f]); 2726 if(obj.hasOwnProperty(f) && (typeof obj[f]=='function' || typeof obj[f]=='object')){ 2727 mapped_methods[f.toLowerCase()] = f; 2728 } 2729 } 2730 2731 return (requested_model) ? mapped_methods[requested_model.toLowerCase()] : ""; 2732 }, 2733 2734 /** 2735 * Get the base URL 2736 * 2737 * @public 2738 * @memberof Cargo.o.Helpers 2739 * 2740 * @param {boolean} [force] Always return the Cargo/url 2741 * @param {boolean} [add_slash] If a slash should be returned with Cargo/url 2742 * 2743 * @return {string} If custom domain, return nothing, otherwise return Cargo/url 2744 * @example Cargo.Helper.GetBaseUrl(force, add_slash); 2745 */ 2746 GetBaseUrl : function(force_url, add_slash) { 2747 if( Cargo.API.Config.is_domain ) { 2748 return (add_slash) ? "/" : ""; 2749 } else { 2750 var url = (force_url) ? Cargo.API.Config.cargo_url : ""; 2751 return (add_slash) ? "/" + url : url; 2752 } 2753 }, 2754 2755 /** 2756 * The base Cargo domain 2757 * 2758 * @memberof Cargo.o.Helpers 2759 * 2760 * @return {string} The Cargo root domain 2761 * @example Cargo.Helper.GetBaseCargoDomain(); 2762 */ 2763 GetBaseCargoDomain : function() { 2764 return (/cargocollective.com/.test(document.location.host) || /persona.co/.test(document.location.host)) ? document.location.host : 'cargocollective.com'; 2765 }, 2766 2767 /** 2768 * The base domain 2769 * 2770 * @memberof Cargo.o.Helpers 2771 * 2772 * @return {string} The active root domain 2773 * @example Cargo.Helper.GetBaseDomain(); 2774 */ 2775 GetBaseDomain : function() { 2776 return (/cargocollective.com/.test(document.location.host)) ? 'cargocollective.com' : document.location.host; 2777 }, 2778 2779 /** 2780 * Gets the current model from the Project collection 2781 * 2782 * @memberof Cargo.o.Helpers 2783 * 2784 * @return {object} The active Project Model 2785 * @example Cargo.Helper.GetCurrentModelFromProjects(); 2786 */ 2787 GetCurrentModelFromProjects : function() { 2788 return Cargo.Collection.Projects.where( { project_url : Cargo.Helper.GetCurrentProjectUrl() } ); 2789 }, 2790 2791 /** 2792 * Get the open Project or Page's type 2793 * 2794 * @memberof Cargo.o.Helpers 2795 * 2796 * @return {string/boolean} String of "page" or "project" or false 2797 * @example Cargo.Helper.GetCurrentPageType(); 2798 */ 2799 GetCurrentPageType : function() { 2800 return Cargo.Model.Project.GetType(); 2801 }, 2802 2803 /** 2804 * Get the open Project's URL 2805 * 2806 * @memberof Cargo.o.Helpers 2807 * 2808 * @return {string/boolean} String of URL or false 2809 * @example Cargo.Helper.GetCurrentProjectUrl(); 2810 */ 2811 GetCurrentProjectUrl : function() { 2812 return Cargo.Model.Project.GetProjectUrl(); 2813 }, 2814 2815 /** 2816 * Get the open project's ID 2817 * 2818 * @memberof Cargo.o.Helpers 2819 * 2820 * @return {string/boolean} String of ID or false 2821 * @example Cargo.Helper.GetCurrentProjectId(); 2822 */ 2823 GetCurrentProjectId : function() { 2824 return Cargo.Model.Project.GetId(); 2825 }, 2826 2827 /** 2828 * Get the current Backbone route 2829 * 2830 * @memberof Cargo.o.Helpers 2831 * 2832 * @return {string} Route name 2833 * @example Cargo.Helper.GetCurrentRoute(); 2834 */ 2835 GetCurrentRoute : function() { 2836 return Backbone.history.fragment; 2837 }, 2838 2839 /** 2840 * Gets the name of the Design 2841 * 2842 * @memberof Cargo.o.Helpers 2843 * 2844 * @return {string} Design name 2845 * @example Cargo.Helper.GetDesignName(); 2846 */ 2847 GetDesignName : function() { 2848 return Cargo.API.Config.design_name; 2849 }, 2850 2851 /** 2852 * Get the domain or the Cargo/url 2853 * 2854 * @memberof Cargo.o.Helpers 2855 * 2856 * @return {string} Domain or URL 2857 * @example Cargo.Helper.GetDomainOrUrl(); 2858 */ 2859 GetDomainOrUrl : function() { 2860 if( Cargo.API.Config.is_domain ) { 2861 return this.GetUrl(); 2862 } else { 2863 return location.pathname.split("/")[1]; 2864 } 2865 }, 2866 2867 /** 2868 * Gets the homepage URL 2869 * Could be Filter, could be search, could be domain. etc 2870 * 2871 * @memberof Cargo.o.Helpers 2872 * 2873 * @param {boolean} [force] Force return of the Cargo/url 2874 * @param {boolean} [add_slash] Prefix a slash to the result 2875 * 2876 * @return {string} URL 2877 * @example Cargo.Helper.GetHomeUrl(force, add_slash); 2878 */ 2879 GetHomeUrl : function(force_url, add_slash) { 2880 var slash = (add_slash) ? "/" : ""; 2881 2882 // Set and Filter 2883 if( Cargo.API.Config.set && Cargo.API.Config.filter) { 2884 if( Cargo.API.Config.is_domain ) { 2885 return slash + Cargo.API.Config.set + "/filter/" + encodeURIComponent(Cargo.API.Config.filter); 2886 } else { 2887 return this.GetBaseUrl(force_url, add_slash) + "/" + Cargo.API.Config.set + "/filter/" + encodeURIComponent(Cargo.API.Config.filter); 2888 } 2889 } 2890 // Route and Filter 2891 else if( Cargo.API.Config.route && Cargo.API.Config.filter) { 2892 if( Cargo.API.Config.is_domain ) { 2893 return Cargo.Helper.GetRouteUrl() + "/filter/" + encodeURIComponent(Cargo.API.Config.filter); 2894 } else { 2895 return this.GetBaseUrl(force_url, false) + Cargo.Helper.GetRouteUrl() + "/filter/" + encodeURIComponent(Cargo.API.Config.filter); 2896 } 2897 } 2898 // Filter only 2899 else if( Cargo.API.Config.filter ) { 2900 if( Cargo.API.Config.is_domain ) { 2901 return slash + "filter/" + encodeURIComponent(Cargo.API.Config.filter); 2902 } else { 2903 return this.GetBaseUrl(force_url, add_slash) + "/filter/" + encodeURIComponent(Cargo.API.Config.filter); 2904 } 2905 2906 } 2907 // Set only 2908 else if( Cargo.API.Config.set ) { 2909 if( Cargo.API.Config.is_domain ) { 2910 return slash + Cargo.API.Config.set; 2911 } else { 2912 return this.GetBaseUrl(force_url, add_slash) + "/" + Cargo.API.Config.set; 2913 } 2914 } 2915 // Route only 2916 else if( Cargo.API.Config.route ) { 2917 if( Cargo.API.Config.is_domain ) { 2918 return Cargo.Helper.GetRouteUrl(); 2919 } else { 2920 // Note: don't send the route with the base because we don't want to load projects 2921 // inside of routes. The projects will redirect to the actual links instead 2922 return this.GetBaseUrl(force_url, add_slash); // + "/" + Cargo.Helper.GetRouteUrl(); 2923 } 2924 } 2925 // Default 2926 else { 2927 return this.GetBaseUrl(force_url, add_slash); 2928 } 2929 2930 }, 2931 2932 /** 2933 * Gets the index from one model from the Project collection 2934 * Returns the index 2935 * 2936 * @memberof Cargo.o.Helpers 2937 * 2938 * @param {Backbone.Model} [model] Backbone model 2939 * @return {number} Index of the model 2940 * @example Cargo.Helper.GetIndexFromModel(model); 2941 */ 2942 GetIndexFromModel : function( model ) { 2943 return Cargo.Collection.Projects.indexOf(model[0]); 2944 }, 2945 2946 /** 2947 * Grabs the value of the editor
2948 * 2949 * @memberof Cargo.o.Helpers 2950 * @return {boolean} 2951 * @example Cargo.Helper.GetIsEditor(); 2952 */ 2953 GetIsEditor : function() { 2954 return Cargo.API.Config.is_editor; 2955 }, 2956 2957 /** 2958 * returns the next page offset 2959 * Calculates this based on the page * limit 2960 * 2961 * @memberof Cargo.o.Helpers 2962 * @return {number} Offset 2963 * @example Cargo.Helper.GetNextPageOffset(); 2964 */ 2965 GetNextPageOffset : function() { 2966 var limit = this.GetPageLimit(); 2967 var page = Cargo.API.Config.current_page; 2968 2969 return (page*limit); 2970 }, 2971 2972 /** 2973 * This returns a new Backbone model of the next Project 2974 * It is a convenience method 2975 * 2976 * @memberof Cargo.o.Helpers 2977 * @return {Backbone.Model} Project model 2978 * @example Cargo.Helper.GetNextProjectModel(); 2979 */ 2980 GetNextProjectModel : function() { 2981 return Cargo.Collection.Projects.findWhere( { project_url : Cargo.Helper.GetNextProjectUrl() } ); 2982 }, 2983 2984 /** 2985 * Returns the next Project's purl 2986 * Note: Will wrap the collection back to 0 2987 * 2988 * @memberof Cargo.o.Helpers 2989 * @return {string} URL 2990 * @example Cargo.Helper.GetNextProjectUrl(); 2991 */ 2992 GetNextProjectUrl : function() { 2993 var model = this.GetCurrentModelFromProjects(); 2994 var index = this.GetIndexFromModel( model ); 2995 var next_index = (index+1) % Cargo.Collection.Projects.length; 2996 var next_model = Cargo.Collection.Projects.at(next_index); 2997 2998 return next_model.get('project_url'); 2999 }, 3000 3001 /** 3002 * Calculates the page offset based on page 3003 * Subtract 1 from the page because the offset starts from 0 3004 * but page starts from 1 3005 * 3006 * @memberof Cargo.o.Helpers 3007 * @return {number} Offset 3008 * @example Cargo.Helper.GetOffsetFromPage(); 3009 */ 3010 GetOffsetFromPage : function( page ) { 3011 var limit = this.GetPageLimit(); 3012 page--; 3013 return (page*limit); 3014 }, 3015 3016 /** 3017 * Gets the limit of Projects per page 3018 * Note, when there is an Index page (like Feed) this might be different. 3019 * 3020 * @memberof Cargo.o.Helpers 3021 * @return {number} Page limit 3022 * @example Cargo.Helper.GetPageLimit(); 3023 */ 3024 GetPageLimit : function() { 3025 var option_limit = Cargo.Model.DisplayOptions.GetPaginationCount(); 3026 3027 if( 3028 option_limit && 3029 Cargo.Model.DisplayOptions.GetIndexRouteName() && 3030 Cargo.Model.DisplayOptions.GetIndexRouteName().toUpperCase() == Cargo.Helper.GetRouteName().toUpperCase() && 3031 Cargo.Model.DisplayOptions.get('index_pagination_count') 3032 ) { 3033 option_limit = Cargo.Model.DisplayOptions.get('index_pagination_count'); 3034 3035 } else if(Cargo.Model.DisplayOptions.get('mobile_pagination_count') && this.isMobile()) { 3036 option_limit = Cargo.Model.DisplayOptions.get('mobile_pagination_count'); 3037 } 3038 3039 return (option_limit) ? option_limit : 10; 3040 }, 3041 3042 /** 3043 * Return the id from the model based on purl 3044 * 3045 * @memberof Cargo.o.Helpers 3046 * @param {string} [purl] URL for the project 3047 * @return {number} Project ID 3048 * @example Cargo.Helper.GetPidFromPurl(purl); 3049 */ 3050 GetPidFromPurl : function( project_url ) { 3051 // Set the new model 3052 var model = Cargo.Collection.Projects.where( { project_url : project_url } ); 3053 3054 if(model[0]) { 3055 return model[0].get('id'); 3056 } else { 3057 return 0; 3058 } 3059 }, 3060 3061 /** 3062 * Calculates previous page offset 3063 * 3064 * @memberof Cargo.o.Helpers 3065 * @return {number} Offset 3066 * @example Cargo.Helper.GetPrevPageOffset(); 3067 */ 3068 GetPrevPageOffset : function() { 3069 var limit = this.GetPageLimit(); 3070 var page = Cargo.API.Config.current_page-1; 3071 3072 return (page*limit); 3073 }, 3074 3075 /** 3076 * This returns a new backbone model of the previous project 3077 * It is a convenience method 3078 * 3079 * @memberof Cargo.o.Helpers 3080 * @return {Backbone.Model} Project model 3081 * @example Cargo.Helper.GetPrevProjectModel(); 3082 */ 3083 GetPrevProjectModel : function() { 3084 return Cargo.Collection.Projects.findWhere( { project_url : Cargo.Helper.GetPrevProjectUrl() } ); 3085 }, 3086 3087 /** 3088 * The url for the previous Project 3089 * 3090 * @memberof Cargo.o.Helpers 3091 * @return {string} URL 3092 * @example Cargo.Helper.GetPrevProjectUrl(); 3093 */ 3094 GetPrevProjectUrl : function() { 3095 var model = this.GetCurrentModelFromProjects(); 3096 var index = this.GetIndexFromModel( model ); 3097 var next_index = (index > 0) ? index-1 : Cargo.Collection.Projects.length - 1; 3098 var next_model = Cargo.Collection.Projects.at(next_index); 3099 3100 return next_model.get('project_url'); 3101 }, 3102 3103 /** 3104 * Returns a page and a single backbone model based on key 3105 * This takes into account the possibility that the model 3106 * exists in a different page then the current one 3107 * 3108 * @memberof Cargo.o.Helpers 3109 * @return {Object} { "page" : 3, "model" : [Object] } 3110 * @example Cargo.Helper.GetProjectModelAndPageByKey(key); 3111 */ 3112 GetProjectModelAndPageByKey : function( key ) { 3113 var offset = this.GetPageLimit(); 3114 var current_page = Cargo.API.Config.current_page;
3115 var page = Math.ceil(key/offset); 3116 var page_offset = 0; 3117 3118 if( !Cargo.Helper.IsAutopaginate() ) { 3119 page_offset = offset - ((page * offset) - key); 3120 page_offset = (page_offset == offset) ? page_offset-1 : page_offset; 3121 page_offset = (page_offset == Cargo.Collection.Projects.length) ? 0 : page_offset; 3122 } else { 3123 page = 0; 3124 page_offset = (key == Cargo.Collection.Projects.length) ? 0 : key; 3125 } 3126 3127 var model = {}; 3128 3129 // Grab from collection ahead of page 1 3130 if(Cargo.Collection.Page[page] && !Cargo.Helper.IsAutopaginate() ) { 3131 model = new Cargo.Model.BaseProject( Cargo.Collection.Page[page][page_offset] ); 3132 } 3133 // Grab from the active collection 3134 else { 3135 model = Cargo.Collection.Projects.at(page_offset); 3136 } 3137 3138 return { 3139 "page" : page, 3140 "model" : model 3141 } 3142 3143 }, 3144 3145 /** 3146 * Return the purl from the model based on pid 3147 * 3148 * @memberof Cargo.o.Helpers 3149 * @param {number} [pid] Project ID 3150 * @return {string} Project URL 3151 * @example Cargo.Helper.GetPurlFromPid(pid); 3152 */ 3153 GetPurlFromPid : function( id ) { 3154 // Set the new model 3155 var model = Cargo.Collection.Projects.where( { id : id } ); 3156 3157 if(model[0]) { 3158 return model[0].get('project_url'); 3159 } else { 3160 return 0; 3161 } 3162 }, 3163 3164 /** 3165 * Get the route name 3166 * 3167 * @memberof Cargo.o.Helpers 3168 * 3169 * @return {string} Route name 3170 * @example Cargo.Helper.GetRouteName(); 3171 */ 3172 GetRouteName : function() { 3173 var route_name = Cargo.API.Config.route_name; 3174 3175 if(/display_options/.test(Cargo.API.Config.route)) { 3176 route_name = (/feed/.test(route_name)) ? Cargo.Model.DisplayOptions.GetFeedRouteName() : Cargo.Model.DisplayOptions.GetIndexRouteName(); 3177 } 3178 3179 return route_name; 3180 }, 3181 3182 /** 3183 * Get the route url 3184 * 3185 * @memberof Cargo.o.Helpers 3186 * 3187 * @return {string} Route name 3188 * @example Cargo.Helper.GetRouteUrl(); 3189 */ 3190 GetRouteUrl : function() { 3191 var route_url = Cargo.API.Config.route_name; 3192 3193 if(/display_options/.test(Cargo.API.Config.route) || route_url == "feed" || route_url == "index") { 3194 route_url = (/feed/.test(route_url)) ? Cargo.Model.DisplayOptions.GetFeedRouteUrl() : Cargo.Model.DisplayOptions.GetIndexRouteUrl(); 3195 } 3196 3197 return route_url; 3198 }, 3199 3200 /** 3201 * Get the currently active set key 3202 * 3203 * @memberof Cargo.o.Helpers 3204 * @return {string} Current set key 3205 * @example Cargo.Helper.GetCurrentSetKey(); 3206 */ 3207 GetSetKey : function() { 3208 return Cargo.Model.Site.GetCurrentSetKey(); 3209 }, 3210 3211 /** 3212 * Returns the name of the current set 3213 * 3214 * @memberof Cargo.o.Helpers 3215 * @return {string} Set name 3216 * @example Cargo.Helper.GetSetName(); 3217 */ 3218 GetSetName : function() { 3219 return Cargo.API.Config.set; 3220 }, 3221 3222 /** 3223 * Returns the start project id 3224 * 3225 * @memberof Cargo.o.Helpers 3226 * @return {number} Start Project ID 3227 * @example Cargo.Helper.GetStartProjectId(); 3228 */ 3229 GetStartProjectId : function() { 3230 return Math.floor(Cargo.Model.Site.GetStartProjectId()); 3231 }, 3232 3233 /** 3234 * Get the total amount of pages 3235 * 3236 * @memberof Cargo.o.Helpers 3237 * @return {number} Total pages 3238 * @example Cargo.Helper.GetTotalPages(); 3239 */ 3240 GetTotalPages : function() { 3241 return Math.ceil( Cargo.Model.DisplayOptions.GetTotalProjects() / Cargo.Helper.GetPageLimit() ); 3242 }, 3243 3244 /** 3245 * Get the total amount of Projects in memory (used for random) 3246 * 3247 * @memberof Cargo.o.Helpers 3248 * @return {number} Total projects in memory 3249 * @example Cargo.Helper.GetTotalProjectsInMemory(); 3250 */ 3251 GetTotalProjectsInMemory : function() { 3252 var current_count = Cargo.Collection.Projects.length; 3253 if( Cargo.Helper.IsAutopaginate() ) { 3254 return current_count; 3255 } 3256 3257 var stored_count = 0; 3258 $.each(Cargo.Collection.Page, function() { stored_count += this.length; }); 3259 return current_count + stored_count; 3260 }, 3261 3262 /** 3263 * Get the Cargo/url 3264 * 3265 * @memberof Cargo.o.Helpers 3266 * @return {string} This site's Cargo/url 3267 * @example Cargo.Helper.GetUrl(); 3268 */ 3269 GetUrl : function() { 3270 // return location.pathname.replace(/^\/([^\/]*).*$/, '$1'); 3271 return Cargo.API.Config.cargo_url; 3272 }, 3273 3274 /** 3275 * Go to a URL on this site. Does a full page refresh 3276 * 3277 * @memberof Cargo.o.Helpers 3278 * @param {String} [relative_path] The relative path of the URL 3279 * @example Cargo.Helper.GoToUrl("filter/category"); 3280 */ 3281 GoToUrl : function(relative_path) { 3282 relative_path = (relative_path.charAt(0) == "/") ? relative_path.substr(1) : relative_path; 3283 var baseurl = Cargo.Helper.GetBaseUrl(true, true); 3284 var location_path = (Cargo.API.Config.is_domain) ? baseurl + relative_path : baseurl + "/" + relative_path; 3285 3286 top.document.location.href = location_path; 3287 }, 3288 3289 /** 3290 * Hides the loading animation 3291 * Fires an event 3292 * Use with: 3293 * <pre><div data-loading="12345"><img src="animation.gif"></div></pre> 3294 * Or: 3295 * <pre><img src="animation.gif" data-loading="global" /></pre> 3296 * 3297 * @memberof Cargo.o.Helpers 3298 * @param {number} [pid] Project ID 3299 * @example Cargo.Helper.HideLoadingAnimation(pid); 3300 */ 3301 HideLoadingAnimation : function( pid ) { 3302 this.Data.is_loading = false;
3303 $("[data-loading]").hide(); 3304 3305 if(typeof Cargo.View.Autopaginate == "object") { 3306 Cargo.View.Autopaginate.$el.hide(); 3307 } 3308 }, 3309 3310 /** 3311 * Checks to see if we are in Admin edit mode 3312 * 3313 * @memberof Cargo.o.Helpers 3314 * @return {boolean} 3315 * @example Cargo.Helper.IsAdminEdit(); 3316 */ 3317 IsAdminEdit : function() { 3318 var is_admin_path = (/\/adminedit/.test(document.location.pathname) || /\/admin/.test(document.location.pathname)); 3319 var has_parent = (parent.location != window.location); 3320 return (is_admin_path || has_parent); 3321 }, 3322 3323 /** 3324 * Checks to see if we are in aAdmin edit mode 3325 * 3326 * @memberof Cargo.o.Helpers 3327 * @return {boolean} 3328 * @example Cargo.Helper.IsAdminEditProject(); 3329 */ 3330 IsAdminEditProject : function() { 3331 return Cargo.API.Config.admin_edit_project; 3332 }, 3333 3334 /** 3335 * Checks to see if this Design autopaginates 3336 * 3337 * @memberof Cargo.o.Helpers 3338 * @return {boolean} 3339 * @example Cargo.Helper.IsAutopaginate(); 3340 */ 3341 IsAutopaginate : function() { 3342 return ( Cargo.View.Autopaginate || $("[data-view='Autopaginate']").length > 0 || $("[data-view='autopaginate']").length > 0); 3343 }, 3344 3345 /** 3346 * Matches an incoming route with the current route 3347 * Used to ensure we are not trying to double load 3348 * 3349 * @memberof Cargo.o.Helpers 3350 * @param {string} [url] URL 3351 * @return {boolean} 3352 * @example Cargo.Helper.IsCurrentRoute(url); 3353 */ 3354 IsCurrentRoute : function( url ) { 3355 // Normalize the url 3356 url = this.NormalizeRouteLink(url); 3357 3358 // Now compare 3359 return (Cargo.Helper.GetCurrentRoute() == url); 3360 }, 3361 3362 /** 3363 * If we are currently on a custom defined route 3364 * 3365 * @memberof Cargo.o.Helpers 3366 * @return {boolean} 3367 * @example Cargo.Helper.IsCustomRoute(); 3368 */ 3369 IsCustomRoute : function() { 3370 return Cargo.API.Config.is_custom_route; 3371 }, 3372 3373 /** 3374 * If we are currently on a Direct Link 3375 * 3376 * @memberof Cargo.o.Helpers 3377 * @return {boolean} 3378 * @example Cargo.Helper.IsDirectLink(); 3379 */ 3380 IsDirectLink : function() { 3381 return Cargo.API.Config.is_direct_link; 3382 }, 3383 3384 /** 3385 * If this is a design without Project history 3386 * 3387 * @memberof Cargo.o.Helpers 3388 * @return {boolean} 3389 * @example Cargo.Helper.IsFeedDesign(); 3390 */ 3391 IsFeedDesign : function() { 3392 return ($("[rel='history']").length == 0) ? true : false; 3393 }, 3394 3395 /** 3396 * If we are currently on a filter link 3397 * 3398 * @memberof Cargo.o.Helpers 3399 * @return {boolean} 3400 * @example Cargo.Helper.IsFilter(); 3401 */ 3402 IsFilter : function() { 3403 return (this.GetActiveFilter() && this.GetActiveFilter().trim() != ""); 3404 }, 3405 3406 /** 3407 * Test for Firefox. Return true | false 3408 * 3409 * @memberof Cargo.o.Helpers 3410 * @return {boolean} 3411 * @example Cargo.Helper.isFirefox(); 3412 */ 3413 isFirefox : function() { 3414 return (/Firefox\/\d+\.\d+/.test(navigator.userAgent)); 3415 }, 3416 3417 /** 3418 * Test for IE. Return true | false 3419 * 3420 * @memberof Cargo.o.Helpers 3421 * @return {boolean} 3422 * @example Cargo.Helper.isIE(); 3423 */ 3424 isIE : function() { 3425 return (/MSIE (\d+\.\d+);/.test(navigator.userAgent)); 3426 }, 3427 3428 /** 3429 * Test for IE8. Return true | false 3430 * Actually, this is just a test for anything < 9 3431 * 3432 * @memberof Cargo.o.Helpers 3433 * @return {boolean} 3434 * @example Cargo.Helper.isIE8(); 3435 */ 3436 isIE8 : function() { 3437 var rv = -1; // Return value assumes failure. 3438 if (this.isIE()) { 3439 var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 3440 if (re.exec(navigator.userAgent) != null) rv = parseFloat( RegExp.$1 ); 3441 } 3442 3443 return (rv < 9 && rv > 0) ? true : false; 3444 }, 3445 3446 /** 3447 * Test for iOS. 3448 * 3449 * @memberof Cargo.o.Helpers 3450 * @return {boolean} 3451 * @example Cargo.Helper.isIOS(); 3452 */ 3453 isIOS : function() { 3454 if (navigator.userAgent.match(/i(Phone|Pod|Pad)/i)) { 3455 return true; 3456 } else { 3457 return false; 3458 } 3459 }, 3460 3461 /** 3462 * Test for all mobile. 3463 * 3464 * @memberof Cargo.o.Helpers 3465 * @return {boolean} 3466 * @example Cargo.Helper.isMobile(); 3467 */ 3468 isMobile : function() { 3469 if(this.isIOS()) return true 3470 else { 3471 var check = false; 3472 (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|
3472sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); 3473 return check; 3474 } 3475 }, 3476 3477 /** 3478 * If we are currently on the homepage 3479 * 3480 * @memberOf Cargo.o.Helpers 3481 * @return {boolean} 3482 * @example Cargo.Helper.IsOnHome(); 3483 */ 3484 IsOnHome : function() { 3485 var on_home = (Cargo.API.Config.set || Cargo.API.Config.filter || Cargo.Helper.GetCurrentRoute() != "") ? false : true; 3486 3487 return on_home; 3488 }, 3489 3490 /** 3491 * If we are currently on a set 3492 * 3493 * @memberof Cargo.o.Helpers 3494 * @return {boolean} 3495 * @example Cargo.Helper.IsOnSet(); 3496 */ 3497 IsOnSet : function() { 3498 return (Cargo.API.Config.set) ? true : false; 3499 }, 3500 3501 /** 3502 * If we are currently on a start project 3503 * 3504 * @memberof Cargo.o.Helpers 3505 * @return {boolean} 3506 * @example Cargo.Helper.IsOnStartProject(); 3507 */ 3508 IsOnStartProject : function() { 3509 return (Cargo.Helper.GetCurrentRoute() == "" && Cargo.API.Config.start_project != null); 3510 }, 3511 3512 /** 3513 * Test for anything less then IE10 3514 * 3515 * @memberof Cargo.o.Helpers 3516 * @return {boolean} 3517 * @example Cargo.Helper.isOldIE(); 3518 */ 3519 isOldIE : function() { 3520 var rv = -1; // Return value assumes failure. 3521 if (this.isIE()) { 3522 var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 3523 if (re.exec(navigator.userAgent) != null) rv = parseFloat( RegExp.$1 ); 3524 } 3525 3526 return (rv <= 9 && rv > 0) ? true : false; 3527 }, 3528 3529 /** 3530 * Called by the router 3531 * This method tests to see if a given route is a permalink 3532 * (loaded by direct link) or if it is loaded by a click 3533 * To determine this, see if the collection data has been loaded yet 3534 * If it has, then this is not a permalink 3535 * 3536 * @memberof Cargo.o.Helpers 3537 * @param {string} [purl] Project URL 3538 * @param {boolean} [from_router] If we are coming from the router or not 3539 * @return {boolean} 3540 * @example Cargo.Helper.IsPermalink(purl, from_router); 3541 */ 3542 3543 IsPermalink : function( purl, from_router ) { 3544 var project_or_route = false; 3545 var start_and_no_project = false; 3546 var collection_loaded = false; 3547 var strip_url = (this.GetRouteUrl()) ? this.GetRouteUrl().replace(/\//,'') : ''; 3548 3549 // If the incoming PURL is a project or route 3550 if( 3551 this.IsCurrentProject(purl) || 3552 this.IsCurrentProjectId(purl) || 3553 this.IsCurrentRoute(purl) || 3554 strip_url == purl || 3555 Cargo.Helper.GetRouteName() == strip_url || 3556 purl == "Website-Offline-message" 3557 ) { 3558 project_or_route = true; 3559 } 3560 3561 // If this is a start project 3562 if( 3563 Cargo.Helper.IsOnStartProject() && 3564 Cargo.Model.Project.GetProjectUrl() != purl 3565 ) { 3566 start_and_no_project = true; 3567 } 3568 3569 // If the collection has been loaded from the server yet 3570 if( 3571 Cargo.Collection.Projects.models.length >= 1 && 3572 Cargo.Collection.Projects.models[0].id 3573 ) { 3574 collection_loaded = true; 3575 } 3576 3577 var project_is_project = (Cargo.Model.Project.GetProjectUrl() == purl || Cargo.Model.Project.GetId() == purl || this.GetRouteName() == purl || this.GetCurrentRoute() == purl || purl == "Website-Offline-message"); 3578 var start_and_no_project = (Cargo.Helper.IsOnStartProject() && Cargo.Model.Project.GetProjectUrl() != purl); 3579 var _final = (!Cargo.API.Config.first_load_finished && (project_is_project || start_and_no_project) ); 3580 3581 /* 3582 * NOTE: If there are issues, it is because of this. Comment the conditional(s) below out to fix 3583 */ 3584 var _final_new = (!Cargo.API.Config.first_load_finished && !collection_loaded && (project_or_route || start_and_no_project) ); 3585 if(!_final_new) { 3586 if(!start_and_no_project && !this.IsAdminEdit() && from_router && !collection_loaded) { 3587 _final = false;
3588 } 3589 } 3590 3591 return _final; 3592 3593 }, 3594 3595 /** 3596 * If we are on a phone 3597 * 3598 * @memberof Cargo.o.Helpers 3599 * @return {boolean} 3600 * @example Cargo.Helper.isPhone(); 3601 */ 3602 isPhone : function() { 3603 var md = new MobileDetect(window.navigator.userAgent); 3604 return (typeof md.phone() == "string"); 3605 }, 3606 3607 IsCurrentProject : function(purl) { 3608 return (Cargo.Model.Project.GetProjectUrl() && purl && Cargo.Model.Project.GetProjectUrl().toLowerCase() == purl.toLowerCase()); 3609 }, 3610 3611 IsCurrentProjectId : function(id) { 3612 return (Cargo.Model.Project.GetId() && id && Cargo.Model.Project.GetProjectUrl() == id); 3613 }, 3614 3615 IsCurrentRoute : function(route) { 3616 return (this.GetRouteName() && route && this.GetRouteName().toLowerCase() == route.toLowerCase()); 3617 }, 3618 3619 /** 3620 * A convenience method for determining if this is an HTML 5 browser 3621 * Note: the latest version of jQuery does not support browser.version 3622 * soooo, we need to figure out something else here 3623 * 3624 * @memberof Cargo.o.Helpers 3625 * @return {boolean} 3626 * @example Cargo.Helper.isRecentBrowser(); 3627 */ 3628 isRecentBrowser : function() { 3629 // If Firefox, and version 3.6 or less 3630 if( $.browser.mozilla && $.browser.version.slice(0,3) >= 1.9 ) { 3631 return true; 3632 // Check webkit, if version 3 or less 3633 } else if ( $.browser.webkit && $.browser.version.substr(0,1) >= 4 ) { 3634 return true; 3635 } else { 3636 return false; 3637 } 3638 }, 3639 3640 /** 3641 * If we are on the search route 3642 * 3643 * @memberof Cargo.o.Helpers 3644 * @return {boolean} 3645 * @example Cargo.Helper.IsSearch(); 3646 */ 3647 IsSearch : function() { 3648 return (this.GetRouteName().toLowerCase() == "search"); 3649 }, 3650 3651 /** 3652 * If the incoming string is a set 3653 * 3654 * @memberof Cargo.o.Helpers 3655 * @param {string} [set] The name of a set 3656 * @return {boolean} 3657 * @example Cargo.Helper.IsSet(set); 3658 */ 3659 IsSet : function(set_name) { 3660 return (Cargo.API.Config.set && Cargo.API.Config.set.toLowerCase() == set_name.toLowerCase()); 3661 }, 3662 3663 /** 3664 * If we are on a solo link 3665 * 3666 * @memberof Cargo.o.Helpers 3667 * @return {boolean} 3668 * @example Cargo.Helper.IsSololink(); 3669 */ 3670 IsSololink : function() { 3671 return Cargo.API.Config.is_solo; 3672 }, 3673 3674 /** 3675 * If we are on a tablet 3676 * 3677 * @memberof Cargo.o.Helpers 3678 * @return {boolean} 3679 * @example Cargo.Helper.isTablet(); 3680 */ 3681 isTablet : function() { 3682 var md = new MobileDetect(window.navigator.userAgent); 3683 return (typeof md.tablet() == "string"); 3684 }, 3685 3686 /** 3687 * If the open project is not in the active collection, put it there 3688 * This is used primarilly for the next/previous project 3689 * 3690 * @memberof Cargo.o.Helpers 3691 * @param {string} [url] URL 3692 * @return {Backbone.Collection} A collection of projects 3693 * @example Cargo.Helper.MakeCurrentProjectCollectionActive(url); 3694 */ 3695 MakeCurrentProjectCollectionActive : function( url ) { 3696 var project_url = (url) ? url : Cargo.Helper.GetCurrentProjectUrl(); 3697 var collection_reset = false; 3698 3699 // If we don't find this project in the collection 3700 if( Cargo.Helper.GetPidFromPurl( Cargo.Helper.GetCurrentProjectUrl() ) == 0) { 3701 var found_collection = this.FindCollectionByProjectUrl( project_url ); 3702 if( found_collection ) { 3703 Cargo.Collection.Projects.reset( found_collection ); 3704 collection_reset = true; 3705 } 3706 } 3707 3708 // Let the updater know if this was reset 3709 return collection_reset; 3710 }, 3711 3712 /** 3713 * Makes a direct link to a project from a project URL 3714 * 3715 * @memberof Cargo.o.Helpers 3716 * @param {string} [purl] Project URL 3717 * @return {string} The direct link to the project 3718 * @example Cargo.Helper.MakeDirectLinkFromPurl(purl); 3719 */ 3720 MakeDirectLinkFromPurl : function( url ) { 3721 var base = Cargo.Helper.GetBaseUrl(true, true); 3722 return (base == '/') ? base + url : base + '/' + url; 3723 }, 3724 3725 /** 3726 * Checks to see if getting a next project is possible on this page 3727 * Will return true / false 3728 * 3729 * @memberof Cargo.o.Helpers 3730 * @return {boolean} 3731 * @example Cargo.Helper.NextProjectNeedsToPaginate(); 3732 */ 3733 NextProjectNeedsToPaginate : function() { 3734 var model = this.GetCurrentModelFromProjects(); 3735 var index = this.GetIndexFromModel( model ); 3736 3737 if( (index+1) == Cargo.Collection.Projects.length && this.GetTotalPages() > 1 ) { 3738 return true; 3739 } else { 3740 return false;
3741 } 3742 }, 3743 3744 /** 3745 * In order for the router to work with both hash and slash URLs 3746 * It needs to have the same links always. This means that we need 3747 * to normalize the link by removing the site URL before routing 3748 * 3749 * @memberof Cargo.o.Helpers 3750 * @param {string} [url] URL 3751 * @return {string} URL 3752 * @example Cargo.Helper.NormalizeRouteLink(url); 3753 */ 3754 NormalizeRouteLink : function( url ) { 3755 if(!url) { 3756 return url; 3757 } 3758 3759 // Sanitize the url 3760 url = url.replace(Cargo.Helper.GetBaseDomain(), "").replace(window.location.protocol + "//",""); 3761 if(url.charAt(0) == "/") { 3762 url = url.replace("/", ""); 3763 } 3764 3765 /* 3766 * We should be left with only the route 3767 * now see if we need to remove the Cargo url 3768 */ 3769 if(url.search(this.GetHomeUrl(true)) == 0) { 3770 url = url.replace(this.GetHomeUrl(true)+"/", ""); 3771 } 3772 3773 return url; 3774 }, 3775 3776 /** 3777 * Checks to see if getting a prev project is possible on this page 3778 * 3779 * @memberof Cargo.o.Helpers 3780 * @return {boolean} 3781 * @example Cargo.Helper.PrevProjectNeedsToPaginate(); 3782 */ 3783 PrevProjectNeedsToPaginate : function() { 3784 var model = this.GetCurrentModelFromProjects(); 3785 var index = this.GetIndexFromModel( model ); 3786 3787 if( index <= 0 && this.GetTotalPages() > 1 ) { 3788 return true; 3789 } else { 3790 return false; 3791 } 3792 }, 3793 3794 /** 3795 * Scroll to the top of the page 3796 * Use a timeout because everything happens too fast without it 3797 * 3798 * @memberof Cargo.o.Helpers 3799 * @example Cargo.Helper.ScrollToTop(); 3800 */ 3801 ScrollToTop : function() { 3802 setTimeout(function() { 3803 // If we are further then 50 from the top, jump to 50 3804 if($(window).scrollTop() > 50) { 3805 window.scrollTo(0, 50); 3806 } 3807 3808 // Smooth scroll the rest of the way 3809 doscroll(0,$(window).scrollTop(),0); 3810 }, 50); 3811 }, 3812 3813 /** 3814 * Scroll to the left of the page (used for horizontal scroll designs) 3815 * Use a timeout because everything happens too fast without it 3816 * 3817 * @memberof Cargo.o.Helpers 3818 * @example Cargo.Helper.ScrollToLeft(); 3819 */ 3820 ScrollToLeft : function(scroll_obj) { 3821 setTimeout(function() { 3822 if(typeof scroll_obj == "object") { 3823 if(scroll_obj instanceof jQuery) { 3824 scroll_obj = scroll_obj[0]; 3825 } 3826 if($(scroll_obj).scrollLeft() > 100) { 3827 scroll_obj.scrollLeft =100; 3828 } 3829 } else { 3830 if($(window).scrollLeft() > 100) { 3831 window.scrollTo(100, 0); 3832 scroll_obj = window; 3833 } 3834 } 3835 3836 // Smooth scroll the rest of the way 3837 doscroll_left(0, $(scroll_obj).scrollLeft(),0, scroll_obj); 3838 }, 50); 3839 }, 3840 3841 /** 3842 * Return the model from the collection based on project id 3843 * 3844 * @memberof Cargo.o.Helpers 3845 * @param {number} [id] ID of the Project 3846 * @return {Backbone.model} Project model 3847 * @example Cargo.Helper.SetActiveProjectFromId(id); 3848 */ 3849 SetActiveProjectFromId : function( id ) { 3850 // remove any appended increments 3851 var id = id.split('-')[0]; 3852 3853 // Set the new model 3854 var model = Cargo.Collection.Projects.where( { id : parseInt(id) } ); 3855 3856 // Set the model in the global scope 3857 Cargo.Model.Project = model[0].toJSON(); 3858 3859 return Cargo.Model.Project; 3860 }, 3861 3862 /** 3863 * Return the model from the collection based on purl 3864 * 3865 * @memberof Cargo.o.Helpers 3866 * @param {string} [purl] URL to the project 3867 * @return {Backbone.model} Project model 3868 * @example Cargo.Helper.SetActiveProjectFromPurl(purl); 3869 */ 3870 SetActiveProjectFromPurl : function( project_url ) { 3871 // Set the new model 3872 var model = Cargo.Collection.Projects.where( { project_url : project_url } ); 3873 3874 // Set the model in the global scope 3875 Cargo.Model.Project = model[0].toJSON(); 3876 3877 return Cargo.Model.Project; 3878 }, 3879 3880 /** 3881 * Shows the loading animation 3882 * Fires an event 3883 * Usage: 3884 * <pre><div data-loading="12345"><img src="animation.gif"></div></pre> 3885 * Or: 3886 * <pre><img src="animation.gif" data-loading="global"></pre> 3887 * 3888 * @memberof Cargo.o.Helpers 3889 * @param {number} [pid] Project ID 3890 * @param {string} [url] URL to the project 3891 * @example Cargo.Helper.ShowLoadingAnimation(pid, url); 3892 */ 3893 ShowLoadingAnimation : function( pid, url ) { 3894 this.Data.is_loading = true; 3895 if( pid ) { 3896 _.delay(function() { 3897 Cargo.Helper.ShowLoadingAnimationById(pid); 3898 }, Cargo.Helper.Data.loading_timout); 3899 3900 } else if(url) { 3901 _.delay(function() { 3902 Cargo.Helper.ShowLoadingAnimationById('page'); 3903 }, Cargo.Helper.Data.loading_timout); 3904 } 3905 $("*[data-loading=global], *[data-loading=global] .retinaSpinner").show(); 3906 }, 3907 3908 /** 3909 * Shows a specific loading animation based on project ID 3910 * 3911 * @memberof Cargo.o.Helpers 3912 * @param {number} [id] ID of the project 3913 * @example Cargo.Helper.ShowLoadingAnimationById(id); 3914 */ 3915 ShowLoadingAnimationById : function(id) { 3916 if(this.Data.is_loading) { 3917 $("*[data-loading="+id+"], *[data-loading="+id+"] .retinaSpinner").show(); 3918 } 3919 }, 3920 3921 /** 3922 * For timeing how long things take internally 3923 * Start and stop is the same call Cargo.Helper.Stopwatch() 3924 * 3925 * @memberof Cargo.o.Helpers 3926 * @example Cargo.Helper.Stopwatch(); 3927 * @private 3928 */ 3929 Stopwatch : function() { 3930 if( !this.startTime ) { 3931 this.startTime = new Date().getMilliseconds(); 3932 } else if( !this.endTime ) { 3933 this.endTime = new Date().getMilliseconds(); 3934 3935 var total = this.endTime - this.startTime; 3936 Cargo.Core.log(total+"ms"); 3937 this.startTime = false;
3938 this.endTime = false; 3939 } 3940 3941 3942 }, 3943 3944 /** 3945 * We can't have wrong case in the URL because all routing is based 3946 * on the correct case. If there is a wrong care 3947 */ 3948 TestUrlCaseAndRedirect: function() { 3949 if( !Cargo.API.Config.is_domain ) { 3950 3951 // Test if the URL is the same case as what we have in the db 3952 var path_url = document.location.pathname.split('/')[1], 3953 url = Cargo.API.Config.cargo_url; 3954 3955 // If there are caps in the url, just keep using what the browser says 3956 if(path_url != url && path_url.toUpperCase() == url.toUpperCase()) { 3957 history.replaceState({}, Cargo.Model.Site.GetWebsiteTitle(), document.location.pathname.replace(path_url, url)); 3958 } 3959 } 3960 } 3961 3962}); 3963 3964/*************************************************************************** 3965 * 3966 * Router 3967 * 3968 **************************************************************************/ 3969Cargo.o.router = Backbone.Router.extend({ 3970 3971 // ========== 3972 // = Routes = 3973 // ========== 3974 3975 routes: { 3976 "" : "Home", 3977 "adminedit" : "Home", 3978 "admin" : "Admin", 3979 "admin/:section" : "Admin", 3980 "preview/:which" : "Home", 3981 "filter/:filter" : "FilterHome", 3982 "filter/:filter/:purl" : "FilterProjectDetail", 3983 ":set/filter/:filter" : "SetFilterHome", 3984 ":set/filter/:filter/:purl" : "SetFilterProjectDetail", 3985 ":purl" : "FirstSlashProxy", 3986 ":purl/solo" : "SoloLink", 3987 ":purl/adminedit" : "AdminEdit", 3988 ":set/:purl" : "SetProjectDetail", 3989 "*actions" : "defaultAction" 3990 }, 3991 3992 // ===================== 3993 // = Handler Functions = 3994 // ===================== 3995 3996 3997 /* 3998 * Catch all for an unhandled event 3999 */ 4000 defaultAction: function(actions) { 4001 // if( this.default != actions ) { 4002 // Cargo.Core.log("Default action: "+actions); 4003 this.route(actions); 4004 // } 4005 }, 4006 4007 /* 4008 * This acts as a router for anything past the first slash 4009 * Make sure it's not the URL, needs to be case insensitive 4010 * It could be a custom route, or a set, but most likely 4011 * it is a project 4012 * :purl 4013 */ 4014 FirstSlashProxy : function( url ) { 4015 if( this.RouteIsHome(url) ) { 4016 this.Home(); 4017 4018 } else if( Cargo.Helper.IsCustomRoute() ) { 4019 this.CustomRoute( url ); 4020 4021 } else if( Cargo.Helper.IsSet(url) ) { 4022 this.Set( url ); 4023 4024 } else { 4025 this.ProjectDetail( url ); 4026 } 4027 }, 4028 4029 /* 4030 * Matches the homepage link 4031 */ 4032 Home : function() { 4033 /* Check for a start project 4034 * Just tell the detail to open */ 4035 if( Cargo.API.Config.start_project && Cargo.API.Config.start_project != 0 ) { 4036 /* Cargo.View.ProjectDetail.$el.show(); */ 4037 this.ProjectDetail( Cargo.API.Config.start_project ); 4038 4039 if(Cargo.View.Main) { 4040 Cargo.View.Main.SetStartProjectClass(); 4041 } 4042 4043 } else if(Cargo.API.Config.offline) { 4044 /* Offline page, render load complete */ 4045 Cargo.Event.trigger("project_load_complete"); 4046 4047 } else { 4048 /* 4049 * By setting the id = 0, the project model is changed 4050 * which fires the ProjectDetail render function 4051 * If the id=0, then the render will close instead of open 4052 * 4053 * Note: this was changed to "clear" and I am not sure 4054 * what implications it has 6/28/13 4055 */ 4056 // Cargo.Model.Project.set( { id : 0 } ); 4057 Cargo.Model.Project.clear(); 4058 4059 Cargo.Event.trigger("homepage_loaded"); 4060 } 4061 }, 4062 4063 /* 4064 * Helper for filter homepage 4065 * filter/:filter 4066 */ 4067 FilterHome : function( filter ) { 4068 // Cargo.Core.log("Filter: "+filter); 4069 Cargo.API.Config.filter = filter; 4070 4071 return this.Home( ); 4072 }, 4073 4074 Admin : function(section) { 4075 if($("#content_frame").length == 0 && typeof require == "function") { 4076 Cargo.Core.loadEditor(); 4077 } 4078 }, 4079 4080 /* 4081 * Helper for filter detail link 4082 * filter/:filter/:purl 4083 */ 4084 FilterProjectDetail : function( filter, purl ) { 4085 // Cargo.Core.log("Filter: "+filter); 4086 Cargo.API.Config.filter = filter; 4087 4088 return this.ProjectDetail( purl ); 4089 }, 4090 4091 /* 4092 * Helper for set filter homepage 4093 * :set/filter/:filter 4094 */ 4095 SetFilterHome : function( set, filter ) { 4096 // Cargo.Core.log("Set: "+set+" Filter: "+filter); 4097 Cargo.API.Config.filter = filter; 4098 4099 return this.Home( ); 4100 }, 4101 4102 /* 4103 * Helper for set filter detail link 4104 * :set/filter/:filter/:purl 4105 */ 4106 SetFilterProjectDetail : function( set, filter, purl ) { 4107 // Cargo.Core.log("Set: "+set+" Filter: "+filter);
4108 Cargo.API.Config.filter = filter; 4109 4110 return this.ProjectDetail( purl ); 4111 }, 4112 4113 /* 4114 * Helper for set detail link 4115 * :set/:purl 4116 */ 4117 SetProjectDetail : function( set, purl ) { 4118 // Cargo.Core.log("Set: "+set+" Project: "+purl); 4119 4120 // This may trigger when the case is not correct on the set, in that case, go home 4121 return ( Cargo.Helper.IsSet(set) || set.toUpperCase() == Cargo.Helper.GetHomeUrl(true).toUpperCase()) ? this.ProjectDetail( purl ) : this.Home(); 4122 }, 4123 4124 4125 /* 4126 * Project detail 4127 * :purl 4128 */ 4129 ProjectDetail : function( purl ) { 4130 // Make sure this is a string 4131 purl += ""; 4132 4133 /* Strip any slashes from the purl */ 4134 purl = purl.replace(/\//g, ""); 4135 4136 // Cargo.Core.log( "Router: ProjectDetail: /"+purl ); 4137 4138 var from_router = true; 4139 var is_perma = Cargo.Helper.IsPermalink(purl, from_router); 4140 4141 4142 if( is_perma ) { 4143 /* 4144 * Do the permalink here 4145 */ 4146 if( Cargo.View.ProjectDetail ) { 4147 Cargo.View.ProjectDetail.Permalink(purl); 4148 } 4149 4150 } else if(purl && purl !== false) { 4151 /* 4152 * If there is no project detail element on screen 4153 * don't ajax load, direct load by document location 4154 */ 4155 if(!Cargo.View.ProjectDetail && !Cargo.API.Config.offline) { 4156 Cargo.Helper.GoToUrl( purl ); 4157 4158 /* 4159 * Get and set the new single project 4160 * This will trigger an event that will automatically 4161 * update the view 4162 */ 4163 } else { 4164 Cargo.API.GetSingleProject( purl ); 4165 } 4166 4167 } 4168 4169 }, 4170 4171 /* 4172 * Custom route 4173 * :route 4174 */ 4175 CustomRoute : function( url ) { 4176 // Cargo.Core.log("Custom route: "+url); 4177 Cargo.Event.trigger("custom_route_loaded", url); 4178 4179 if( Cargo.Helper.IsSet(url) ) { 4180 // Cargo.Core.log("Set on custom route: "+url); 4181 Cargo.Event.trigger("set_loaded", url); 4182 } 4183 }, 4184 4185 /* 4186 * Set 4187 * :set 4188 */ 4189 Set : function( url ) { 4190 // Cargo.Core.log("Set: "+url); 4191 Cargo.Event.trigger("set_loaded", url); 4192 4193 }, 4194 4195 /* 4196 * Solo link is a helper for ProjectDetail 4197 * :purl/solo 4198 */ 4199 SoloLink : function( purl ) { 4200 Cargo.API.Config.is_solo = true; 4201 Cargo.Event.trigger("solo_link_loaded", location.pathname); 4202 return this.ProjectDetail( purl ); 4203 }, 4204 4205 /* 4206 * Admin edit is a helper for ProjectDetail 4207 * :purl/adminedit 4208 */ 4209 AdminEdit : function( purl ) { 4210 Cargo.API.Config.admin_edit_project = true; 4211 return this.ProjectDetail( purl ); 4212 }, 4213 4214 4215 4216 /* 4217 * Rewrite the route Regex to allow for case insensitivity and trailing slash support 4218 * Note: This was copied directly from backbone and added the "i" param 4219 * as well as \/? conditional with the second replace(regex) 4220 */ 4221 _routeToRegExp: function (route) { 4222 route = route.replace(/[-{}[\]+?.,\\^$|#\s]/g, '\\$&') 4223 .replace(/\((.*?)\)/g, '(?:$1)?') 4224 .replace(/:\w+/g, '([^\/]+\/?)') 4225 .replace(/\*\w+/g, '(.*?)'); 4226 4227 return new RegExp('^' + route + '$', 'i'); 4228 }, 4229 4230 /* 4231 * Make a shorthand reference for adding a route with Backbone 4232 * Also update this to include all variants possible 4233 * This includes trailing slash and query strings 4234 */ 4235 addroute : function( route, name, callback ) { 4236 var variants = [ 4237 '', // normal route 4238 '/', // trailing slash 4239 '?*querystring', // querystrings 4240 '/?*querystring' // trailing slash + querystrings 4241 ]; 4242 4243 // Bind a route for each of the variants 4244 for (var i=0; i < variants.length; i++) { 4245 var variant = variants[i]; 4246 // Backbone.Router.prototype.route.call(this, route+variant, name, callback); 4247 this.route( route+variant, name, callback ); 4248 } 4249 }, 4250 4251 RouteIsHome : function(url) { 4252 4253 var is_home = false; 4254 // If the incoming url matches the normalized Cargo url 4255 if( !Cargo.API.Config.is_domain && url.toLowerCase() == Cargo.Helper.GetDomainOrUrl().toLowerCase() ) { 4256 // Make sure this is not a project called the same as the URL 4257 if(document.location.pathname.replace("/").split("/").length <= 1) { 4258 is_home = true; 4259 } 4260 } 4261 4262 return is_home; 4263 } 4264 4265 4266}); 4267 4268 4269/*************************************************************************** 4270 * 4271 * Templates 4272 * This is the template manager function file 4273 * It loads and caches tempaltes 4274 * Uses require.js to async load in templates 4275 * 4276 **************************************************************************/ 4277 4278Cargo.o.templates = Backbone.View.extend({ 4279 /* Data store for templates */ 4280 Data : { }, 4281 4282 /** 4283 * Gets a template based on a name 4284 * If one is not available in memory, async load it 4285 */ 4286 Get : function( which, file_path ) { 4287 which = Cargo.Helper.GetBackboneCaseInsensitive(this.Data, which); 4288 4289 if( this.Data[which] && this.Data[which].template ) { 4290 return this.Data[which].template; 4291 } else { 4292 this.Load( which, file_path ); 4293 } 4294 }, 4295 4296 /** 4297 * Gets a template element based on a name 4298 * If one is not available in memory, async load it 4299 */ 4300 GetEl : function( which ) { 4301 which = Cargo.Helper.GetBackboneCaseInsensitive(this.Data, which); 4302 4303 if( this.Data[which] ) { 4304 return this.Data[which].element; 4305 } else { 4306 this.Load( which ); 4307 } 4308 }, 4309 4310 /** 4311 * Add a template to the list 4312 * Note: This does not load the template view 4313 */ 4314 Add : function( template, element, dataset, setname ) {
4315 Cargo.Template.Data[template] = { 4316 dataset : dataset, 4317 setname : setname, 4318 element : element 4319 } 4320 }, 4321 4322 /** 4323 * Loops through the dom to find view stubs 4324 * Seeks out the "data-view" attribute, which can be associated with any 4325 * html element 4326 */ 4327 FindAndAddViewStubs : function( ) { 4328 var self = this; 4329 4330 $("[data-view]").each(function() { 4331 var template = $(this).data("view"); 4332 var setname = $(this).data("set"); 4333 var el = ( $(this).attr("id") ) ? "#" + $(this).attr("id") : "." + $(this).attr("class"); 4334 /* 4335 * For the dataset, find if it is collection or model 4336 * If it does not exist, then use Default Collection 4337 */ 4338 // var dataset = $(this).data("set"); 4339 var dataset = Cargo.Collection.Default; 4340 4341 if(setname) { 4342 var requested_collection = Cargo.Helper.GetBackboneCaseInsensitive(Cargo.Collection, setname); 4343 var requested_model = Cargo.Helper.GetBackboneCaseInsensitive(Cargo.Model, setname); 4344 4345 if( requested_collection ) { 4346 dataset = Cargo.Collection[requested_collection]; 4347 4348 } else if( requested_model ) { 4349 dataset = Cargo.Model[requested_model]; 4350 } 4351 } 4352 4353 /* Add the template to the stack */ 4354 Cargo.Template.Add( template, el, dataset, setname ); 4355 }); 4356 }, 4357 4358 /** 4359 * Loads a single template into memory 4360 * 4361 * Note: For api routes that are not on this domain (ie: api.cargo) 4362 * then we must load the design like a variable with the "true" flag 4363 * Otherwise, load the template like normal. 4364 */ 4365 Load : function( which, file_path ) { 4366 if(!file_path) { 4367 var file_name = which + ".handlebars"; 4368 // file_path = Cargo.API.GetDesignAndFile(file_name, true); 4369 file_path = Cargo.API.GetDesignAndFile(file_name, false); 4370 } 4371 4372 if( !Cargo.Template.Data[which] || !Cargo.Template.Data[which].loading ) { 4373 if( !Cargo.Template.Data[which] ) { 4374 Cargo.Template.Data[which] = { }; 4375 } 4376 4377 Cargo.Template.Data[which].loading = true; 4378 4379 $.loadFiles( file_path ).done( function( template ) { 4380 4381 /* 4382 * If no value for template was passed, require loaded it like a script 4383 */ 4384 if( template ) { 4385 /* 4386 * Evaluate the response, it may come as a string 4387 */ 4388 try { 4389 var template = eval(template); 4390 } catch(err) { 4391 // nothing 4392 } 4393 Cargo.Template.Data[which].template = template; 4394 } else { 4395 /* 4396 * The name of the output variable is = to the file name, without / & . 4397 */ 4398 var template_var = "templatesdesign" + file_name.replace(/(\/|\.)/g, ""); 4399 Cargo.Template.Data[which].template = window[template_var]; 4400 } 4401 4402 /* Fire an event notifying of the newly loaded template */ 4403 Cargo.Event.trigger("template_loaded", which ); 4404 4405 return Cargo.Template.Data[which].template; 4406 }); 4407 } 4408 }, 4409 4410 /** 4411 * Load all non-loaded template into memory async 4412 * Loops through the template list and loads any template 4413 * that has yet to be loaded (this is determined if the "template" var is blank) 4414 */ 4415 LoadAll : function( ) { 4416 for(key in this.Data) { 4417 if( typeof(this.Data[key].template) == "undefined" ) { 4418 this.Load( key ); 4419 } 4420 } 4421 }, 4422 4423 /** 4424 * Loops through the templates and checks if they have 4425 * A view already loaded for them 4426 * If not, it will load a view 4427 * @note: this is not really ideal like this 4428 * @note: perhaps have a view controller 4429 */ 4430 LoadFoundViews : function() { 4431 /* 4432 * Loop through the templates and check to see if a view 4433 * has been init yet. If not, do it 4434 */ 4435 for(key in this.Data) { 4436 var requested_view = Cargo.Helper.GetBackboneCaseInsensitive(Cargo.o.view, key); 4437 if( 4438 typeof(Cargo.o.view[requested_view]) == "function" && 4439 typeof(Cargo.View[requested_view]) == "undefined" 4440 ) { 4441 4442 var self = this; 4443 /* Handle the case for multiple of the same view on the page */ 4444 $("[data-view='"+key+"']").each(function(i) { 4445 var el = ( $(this).attr("id") ) ? "#" + $(this).attr("id") : "." + $(this).attr("class"); 4446 var final_view = (i>0) ? requested_view+"_"+i: requested_view; 4447 4448 Cargo.View[final_view] = new Cargo.o.view[requested_view]({ 4449 el : el, 4450 collection : self.Data[key].dataset, 4451 template : self.Data[key].template, 4452 className : final_view 4453 }); 4454 }); 4455 4456 } else { 4457 if( typeof(Cargo.View[requested_view]) == "function" ) { 4458 Cargo.Core.log("A view for "+requested_view+" already loaded"); 4459 4460 } else if(typeof(Cargo.o.view[requested_view]) == "undefined") { 4461 /* If there is no view, then the user likely 4462 * just added data-view="something" 4463 * In this case, make a view based on our default */ 4464 Cargo.View[key] = new Cargo.o.view.Default({ 4465 el : this.Data[key].element, 4466 collection : this.Data[key].dataset, 4467 template : key, 4468 datasetname : this.Data[key].setname, 4469 className : key 4470 }); 4471 } 4472 } 4473 } 4474 } 4475}); 4476 4477 4478/*************************************************************************** 4479 * 4480 * Addition to Cargo core functionality 4481 * 4482 * The slideshow class 4483 * Cargo.Core.Slideshow.InitSlideshow(); 4484 * 4485 * Created on 6/26/13 for 2.0 4486 * 4487 **************************************************************************/ 4488 4489Cargo.Core.Slideshow = { 4490 4491 uid: 0, 4492 4493 InitSlideshow : function() { 4494 var self = this; 4495
4496 $(".slideshow").not('[data-slideshow-initialized]').each(function() { 4497 // Shorthand for display options 4498 var dop = Cargo.Model.DisplayOptions; 4499 4500 var opts = { 4501 // Transitions 4502 "transition" : dop.GetSlideTransition(), 4503 "transition_duration" : dop.GetSlideTransitionDuration(), 4504 4505 // Autoplay 4506 "autoplay" : dop.GetSlideAutoPlay().enabled, 4507 "autoplay_duration" : dop.GetSlideAutoPlay().delay, 4508 4509 // Display 4510 "navigation" : dop.GetSlideTextNav().enabled, 4511 "thumbnails" : dop.GetSlideThumbnails().enabled, 4512 "toggle_thumbnails" : dop.GetSlideToggleThumbLink().enabled, 4513 "default_thumbnails" : dop.GetSlideDefaultThumbs(), 4514 "square_thumbnails" : false 4515 }; 4516 4517 $(this).attr('data-id', $(this).attr('data-id') + '-' + (self.uid++)); 4518 $(this).cargoSlideshow(opts); 4519 4520 }); 4521 }, 4522 4523 ShowCaption : function() { 4524 4525 }, 4526 4527 // this is where slideshows register theirselves 4528 SlideshowObjects : { 4529 4530 } 4531} 4532 4533/************************************************************************** 4534 * 4535 * Addition to Cargo core functionality 4536 * 4537 * The handlebars rendering class 4538 * 4539 * Usage: 4540 * Cargo.Core.Handlebars.Render(); 4541 * 4542 **************************************************************************/ 4543 4544Cargo.Core.Handlebars = { 4545 /** 4546 * Uses handlebars to render a template and data model 4547 * registers any helpers that need to go with the render 4548 * used by all views that render data 4549 * returns the compiled markup 4550 */ 4551 Render : function(template, data_model) { 4552 // Get the compiled template 4553 var compiled = Handlebars.compile( template ); 4554 4555 /* 4556 * Register partials 4557 */ 4558 Handlebars.registerPartial("loading_animation", $("#loading_animation").html()); 4559 4560 /* 4561 * Register all helpers 4562 */ 4563 this.RegisterHelper_Asset(); 4564 this.RegisterHelper_AssetContents(); 4565 this.RegisterHelper_Print(); 4566 this.RegisterHelper_IfAny(); 4567 this.RegisterHelper_IfAll(); 4568 this.RegisterHelper_IfEqual(); 4569 this.RegisterHelper_UnlessAny(); 4570 this.RegisterHelper_UnlessAll(); 4571 this.RegisterHelper_IfOption(); 4572 this.RegisterHelper_UnlessOption(); 4573 this.RegisterHelper_DisplayOptions(); 4574 this.RegisterHelper_Truncate(); 4575 4576 /* 4577 * Finalize with the model 4578 */ 4579 var markup = compiled( data_model ); 4580 4581 return markup; 4582 }, 4583 4584 /** 4585 * This method is just the same as above 4586 * However, this render only uses the assets helper and 4587 * nothing else. It is used for rendering the CSS content 4588 * from the preview panel 4589 */ 4590 RenderOnlyAssets : function(template) { 4591 var compiled = Handlebars.compile( template ); 4592 this.RegisterHelper_Asset(); 4593 var markup = compiled(); 4594 return markup; 4595 }, 4596 4597 /* 4598 * Register a helper for assets 4599 */ 4600 RegisterHelper_Asset : function() { 4601 Handlebars.registerHelper('asset', function(options) { 4602 var asset = options.fn(this); 4603 var model = Cargo.Collection.Assets.where( { filename : asset } )[0]; 4604 4605 return (model) ? Cargo.Model.Site.GetAssetPath(model.get('filename'), model.get('id')) : ""; 4606 }); 4607 }, 4608 4609 /* 4610 * Register a helder for assets 4611 */ 4612 RegisterHelper_AssetContents : function() { 4613 Handlebars.registerHelper('asset_contents', function(options) { 4614 var asset = options.fn(this); 4615 var model = Cargo.Collection.Assets.where( { filename : asset } )[0]; 4616 4617 var file = (model) ? Cargo.Model.Site.GetAssetPath(model.get('filename'), model.get('id')) : ""; 4618 4619 $.get(file, function(data) { 4620 // Try to replace immediately 4621 $("file[data-url='"+file+"']").after(data).remove(); 4622 4623 // Also try a little later, in case the DOM has not been updated 4624 setTimeout(function() { 4625 $("file[data-url='"+file+"']").after(data).remove(); 4626 }, 500); 4627 4628 }, 'text'); 4629 4630 return '<file data-url="'+file+'"></file>'; 4631 }); 4632 }, 4633 4634 /* 4635 * Register a helper for print 4636 */ 4637 RegisterHelper_Print : function() { 4638 Handlebars.registerHelper('print', function(context, options) { 4639 var wrapper_head = wrapper_tail = ""; 4640 if(context == "project.content" || context == "content") { 4641 wrapper_head = "<projectcontent>"; 4642 wrapper_tail = "</projectcontent>"; 4643 } 4644 context = context.replace("project.",""); 4645 4646 var model = (typeof this.project == "object") ? this.project : this; 4647 4648 if(Cargo.Model.Site.GetUseHiRes()) { 4649 if(model[context + '_2x'] != null){ 4650 context += "_2x"; 4651 } 4652 } 4653 4654 return new Handlebars.SafeString(wrapper_head + model[context] + wrapper_tail); 4655 }); 4656 }, 4657 4658 /* 4659 * Register a helper for if_any 4660 */ 4661 RegisterHelper_IfAny : function() { 4662 Handlebars.registerHelper('if_any', function() { 4663 var any = false;
4664 var options = arguments[arguments.length-1]; 4665 4666 for (var i = arguments.length - 2; i >= 0; i--) { 4667 if( arguments[i] ) { 4668 any = true; 4669 break; 4670 } 4671 } 4672 return (any) ? options.fn(this) : options.inverse(this); 4673 }); 4674 }, 4675 4676 /* 4677 * Register a helper for if_all 4678 */ 4679 RegisterHelper_IfAll : function() { 4680 Handlebars.registerHelper('if_all', function() { 4681 var all = true; 4682 var options = arguments[arguments.length-1]; 4683 4684 for (var i = arguments.length - 2; i >= 0; i--) { 4685 if( !arguments[i] ) { 4686 all = false; 4687 break; 4688 } 4689 } 4690 4691 return (all) ? options.fn(this) : options.inverse(this); 4692 }); 4693 }, 4694 4695 /* 4696 * Register a helper for if_equal 4697 */ 4698 RegisterHelper_IfEqual : function() { 4699 Handlebars.registerHelper('if_equal', function(value_one, value_two, options) { 4700 if (arguments.length != 3) { 4701 return ''; 4702 } 4703 return (value_one == value_two) ? options.fn(this) : options.inverse(this); 4704 }); 4705 }, 4706 4707 /* 4708 * Register a helper for unless_any 4709 */ 4710 RegisterHelper_UnlessAny : function() { 4711 Handlebars.registerHelper('unless_any', function() { 4712 var any = false; 4713 var options = arguments[arguments.length-1]; 4714 4715 for (var i = arguments.length - 2; i >= 0; i--) { 4716 if( !arguments[i] ) { 4717 any = true; 4718 break; 4719 } 4720 } 4721 return (any) ? options.fn(this) : options.inverse(this); 4722 }); 4723 }, 4724 4725 /* 4726 * Register a helper for unless_all 4727 */ 4728 RegisterHelper_UnlessAll : function() { 4729 Handlebars.registerHelper('unless_all', function() { 4730 var all = true; 4731 var options = arguments[arguments.length-1]; 4732 4733 for (var i = arguments.length - 2; i >= 0; i--) { 4734 if( arguments[i] ) { 4735 all = false; 4736 break; 4737 } 4738 } 4739 return (all) ? options.fn(this) : options.inverse(this); 4740 }); 4741 }, 4742 4743 /* 4744 * Register a helper for display options 4745 */ 4746 RegisterHelper_DisplayOptions : function() { 4747 Handlebars.registerHelper('displayoptions', function(context, options) { 4748 // Split the context into args 4749 var args = context.replace('display_options.','').split("."); 4750 // Find out how many options 4751 var count = args.length; 4752 // Assume we are not passing this condition 4753 var condition = false; 4754 4755 if(count == 1) { 4756 condition = Cargo.Model.DisplayOptions.get(args[0]); 4757 } else if(count == 2) { 4758 condition = Cargo.Model.DisplayOptions.get(args[0])[args[1]]; 4759 } else if(count == 3) { 4760 condition = Cargo.Model.DisplayOptions.get(args[0])[args[1]][args[2]]; 4761 } else { 4762 condition = Cargo.Model.DisplayOptions.get(args[0]); 4763 } 4764 4765 return (condition) ? options.fn(this) : options.inverse(this); 4766 }); 4767 4768 }, 4769 4770 /* 4771 * Register a helper for display options 4772 */ 4773 RegisterHelper_IfOption : function() { 4774 Handlebars.registerHelper('if_option', function(context, options) { 4775 // Split the context into args 4776 var args = context.replace('display_options.','').split("."); 4777 // Find out how many options 4778 var count = args.length; 4779 // Assume we are not passing this condition 4780 var condition = false; 4781 4782 if(count == 1) { 4783 condition = Cargo.Model.DisplayOptions.get(args[0]); 4784 } else if(count == 2) { 4785 condition = Cargo.Model.DisplayOptions.get(args[0])[args[1]]; 4786 } else if(count == 3) { 4787 condition = Cargo.Model.DisplayOptions.get(args[0])[args[1]][args[2]]; 4788 } else { 4789 condition = Cargo.Model.DisplayOptions.get(args[0]); 4790 } 4791 4792 return (condition) ? options.fn(this) : options.inverse(this); 4793 }); 4794 4795 }, 4796 4797 /* 4798 * Register a helper for display options 4799 */ 4800 RegisterHelper_UnlessOption : function() { 4801 Handlebars.registerHelper('unless_option', function(context, options) { 4802 // Split the context into args 4803 var args = context.replace('display_options.','').split("."); 4804 // Find out how many options 4805 var count = args.length; 4806 // Assume we are not passing this condition 4807 var condition = false; 4808 4809 if(count == 1) { 4810 condition = Cargo.Model.DisplayOptions.get(args[0]); 4811 } else if(count == 2) { 4812 condition = Cargo.Model.DisplayOptions.get(args[0])[args[1]]; 4813 } else if(count == 3) { 4814 condition = Cargo.Model.DisplayOptions.get(args[0])[args[1]][args[2]]; 4815 } else { 4816 condition = Cargo.Model.DisplayOptions.get(args[0]); 4817 } 4818 4819 return (!condition) ? options.fn(this) : options.inverse(this); 4820 }); 4821 4822 }, 4823 4824 RegisterHelper_Truncate : function() { 4825 Handlebars.registerHelper ('truncate', function (str, len) { 4826 if (str.length > len && str.length > 0) { 4827 var new_str = str + " "; 4828 new_str = str.substr (0, len); 4829 new_str = str.substr (0, new_str.lastIndexOf(" ")); 4830 new_str = (new_str.length > 0) ? new_str : str.substr (0, len); 4831 4832 return new Handlebars.SafeString ( new_str +'...' ); 4833 } 4834 return str; 4835 }); 4836 } 4837} 4838 4839/************************************************************************** 4840 * 4841 * Addition to Cargo core functionality 4842 * 4843 * The video class 4844 * This method is a replica of the one found in site.package 4845 * The "reverse fallback" -- Flash object and HTML5 video tags are both housed 4846 * within a div.video_component. The CheckForVideo() method determines who gets 4847 * to stay, in essence, and removes the unused tags. 4848 * 4849 * Usage: 4850 * Cargo.Core.Video.CheckForVideo(); 4851 * 4852 **************************************************************************/ 4853 4854Cargo.Core.Video = { 4855 Config: { 4856 DEBUG: false, 4857 dependencies: [ 4858 '/_css/mediaplayer.css', 4859 '/_jsapps/_libs/jquery-ui-1.10.3.custom.min.js', 4860 '/_js/cargo.video.package.js' 4861 ], 4862 volume_default: 0.75, 4863 volume: 1, 4864 muted: false 4865 }, 4866 4867/** 4868 * DEBUG 4869 * 4870 * Controlled by Config.DEBUG, MightForceHTML5() and IsForcingHTML5() modify, 4871 * dependent upon the presence of the cargo.video.forcedHTML5 cookie, the 4872 * behavior of DetectPlugin() and DestroyFallbackTags() - always displaying 4873 * the video elements. 4874 * 4875 * ForceHTML5() controls the cargo.video.forcedHTML5 cookie and enables video 4876 * elements accordingly. 4877 */ 4878 MightForceHTML5: function() { 4879 return this.Config.DEBUG; 4880 }, 4881 4882 IsForcingHTML5: function() { 4883 if (this.Config.is_forcing_HTML5 !== undefined) 4884 return this.Config.is_forcing_HTML5; 4885 4886 var forcing_HTML5 = $.cookie('cargo.video.forcedHTML5') || false;
4887 if (forcing_HTML5) 4888 this.Config.has_flash_plugin = false; 4889 4890 this.Config.is_forcing_HTML5 = forcing_HTML5; 4891 return forcing_HTML5; 4892 }, 4893 4894 ForceHTML5: function() { 4895 var should_force_HTML5 = $.cookie('cargo.video.forcedHTML5') || false; 4896 this.Config.force_HTML5 = ! should_force_HTML5; 4897 if (this.Config.force_HTML5) { 4898 $.cookie('cargo.video.forcedHTML5', true, {expires: 30, path: '/'}); 4899 this.Config.has_flash_plugin = false; 4900 this.CheckForVideo(); 4901 } else { 4902 $.cookie('cargo.video.forcedHTML5', null, {path: '/'}); 4903 window.location.reload(); 4904 } 4905 }, 4906 4907/** 4908 * Primary method, determines if there are videos present and, if so, if there 4909 * is a need to "reverse fallback" to an HTML5 player. Upon fallback, loads the 4910 * related CSS and javascript should they not already exist, calling back to 4911 * itself recursively, rearranging and removing Flash and HTML5 elements, and 4912 * finally applying the HTML5 player controls. 4913 * 4914 * @param video_js_has_loaded Boolean, true on recursive call after load of external CSS and javascript files 4915 */ 4916 CheckForVideo: function(video_js_has_loaded) { 4917 if (!this.DetectVideos()) 4918 return; 4919 4920 if (this.DetectPlugin()) { 4921 this.DestroyFallbackTags(); 4922 return; 4923 } 4924 4925 // Load video-related javascript here, but only once 4926 if (this.Config.has_video_js === undefined) { 4927 if (video_js_has_loaded) { 4928 this.Config.has_video_js = true; 4929 } else { 4930 if (this.Config.is_loading_video_js !== undefined) 4931 return; 4932 4933 this.Config.is_loading_video_js = true; 4934 4935 // Actual loading of external dependencies 4936 $.loadFiles(this.Config.dependencies).done(function() { 4937 Cargo.Core.Video.CheckForVideo(true); 4938 }); 4939 4940 return; 4941 } 4942 } 4943 4944 // Culling of the object tags, displaying of the video tags 4945 $('.video_component').each(function() { 4946 $(this).children('object').remove(); 4947 4948 var video_tag = $(this).children('.video_tag'); 4949 var player = video_tag.children(); 4950 video_tag.css({ 4951 display: 'inline-block', 4952 width: player.attr('data-video-width'), 4953 height: player.attr('data-video-height') 4954 }); 4955 4956 // Apply jMediaelement controls after javascript has loaded 4957 if (Cargo.Core.Video.Config.has_video_js) 4958 Cargo.Core.Video.ConfigureMediaPlayer(player); 4959 }); 4960 }, 4961 4962/** 4963 * Application of jMediaelement controls to video tags 4964 */ 4965 ConfigureMediaPlayer: function(player) { 4966 // Don't call video player methods more than once 4967 if (player.attr('data-video-configured') !== undefined) 4968 return; 4969 4970 player.attr('data-video-configured', 'true'); 4971 4972 // Grab volume settings from cookies or defaults 4973 this.Config.volume = $.cookie('cargo.video.volume') || this.Config.volume_default; 4974 this.Config.muted = ($.cookie('cargo.video.muted')) ? true : false; 4975 4976 // Apply jMediaelement controls, custom volume logic 4977 player.jmeInit().jmeProp('controlbar', true); 4978 player.children('video').each(function(index, video) { 4979 $(video).bind('volumechange', function() { 4980 var control = $('.mute-unmute'); 4981 var volume = Number($(this).jmeValue('volume')); 4982 var muted = $(this).jmeValue('muted'); 4983 4984 if (volume > 0.66) { 4985 control.removeClass('volume-medium volume-low').addClass('volume-high'); 4986 } else if (volume <= 0.66 && volume > 0.33) { 4987 control.removeClass('volume-high volume-low state-mute state-unmute').addClass('volume-medium'); 4988 } else if (volume <= 0.33 && volume > 0.01) { 4989 control.removeClass('volume-high volume-medium state-mute state-unmute').addClass('volume-low'); 4990 } else { 4991 control.removeClass('volume-high volume-medium volume-low').addClass('state-unmute'); 4992 } 4993 4994 if (muted) { 4995 control.addClass('state-unmute'); 4996 $.cookie('cargo.video.muted', true, {expires: 30, path: '/'}); 4997 } else { 4998 $.cookie('cargo.video.muted', null, {path: '/'}); 4999 } 5000 5001 $.cookie('cargo.video.volume', volume, {expires: 30, path: '/'}); 5002 }); 5003 5004 video.volume = Cargo.Core.Video.Config.volume; 5005 video.muted = Cargo.Core.Video.Config.muted; 5006 }); 5007 }, 5008 5009/** 5010 * Determine if the browser supports and has the Flash plugin installed 5011 * 5012 * @return Boolean, whether detected 5013 */ 5014 DetectPlugin: function() { 5015 // Only check for Flash once 5016 if (this.Config.has_flash_plugin !== undefined) 5017 return this.Config.has_flash_plugin; 5018 5019 // During debug, always report a lack of Flash 5020 if (this.MightForceHTML5()) { 5021 if (this.IsForcingHTML5()) 5022 return false;
5023 } 5024 5025 var flash_was_detected = false; 5026 if (typeof navigator.plugins != 'undefined' && typeof navigator.plugins['Shockwave Flash'] == 'object') { 5027 var d = navigator.plugins['Shockwave Flash'].description; 5028 if (d && !(typeof navigator.mimeTypes != 'undefined' && navigator.mimeTypes['application/x-shockwave-flash'] && !navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin)) 5029 flash_was_detected = true; 5030 } else if (typeof window.ActiveXObject != 'undefined') { 5031 try { 5032 if ((new ActiveXObject('ShockwaveFlash.ShockwaveFlash'))) 5033 flash_was_detected = true; 5034 } catch(e) {} 5035 } 5036 5037 this.Config.has_flash_plugin = flash_was_detected; 5038 return flash_was_detected; 5039 }, 5040 5041/** 5042 * Get a count of the div.video_component elements, confirm if there is more 5043 * than one present 5044 * 5045 * @return Boolean, true if one or more 5046 */ 5047 DetectVideos: function() { 5048 return ($('.video_component').length > 0); 5049 }, 5050 5051/** 5052 * Remove the video tag parent div, even though it is not displayed, just to be 5053 * extra, extra safe 5054 */ 5055 DestroyFallbackTags: function() { 5056 // During debug, do not remove the video tags 5057 if (this.MightForceHTML5()) 5058 return; 5059 5060 if (this.Config.was_destroyed !== undefined) 5061 return; 5062 5063 this.Config.was_destroyed = true; 5064 $('.video_component .video_tag').each(function(index, value) { 5065 $(this).remove(); 5066 }); 5067 } 5068} 5069/* 5070 * End Cargo.Core.Video 5071 */ 5072 5073 5074/************************************************************************** 5075 * 5076 * Addition to Cargo core functionality 5077 * 5078 * The audio class 5079 * This method is a replica of the one found in site.package 5080 * 5081 * Usage: 5082 * Cargo.Core.Audio.CheckForAudio(); 5083 * 5084 **************************************************************************/ 5085 5086// This value is needed by sound manager and is global because of legacy 5087var start_sound = 5088 this_play = false; 5089 5090Cargo.Core.Audio = { 5091 Config: { 5092 has_audio_js : false, 5093 dependencies: [ 5094 '/_jsapps/_libs/jquery-ui-1.10.3.custom.min.js', 5095 '/_js/soundmanager/api/soundmanager2-nodebug-jsmin.js' 5096 ] 5097 }, 5098 5099 /** 5100 * Primary method, determines if there are audio players present and, if so, load them 5101 * 5102 * @param audio_js_has_loaded Boolean, true on recursive call after load of external CSS and javascript files 5103 */ 5104 CheckForAudio : function() { 5105 if (!this.DetectAudioPlayer()) 5106 return; 5107 5108 this.UnloadAudio(); 5109 this.InitButtons(); 5110 this.InitPlayerSize(); 5111 if(Cargo.Helper.isMobile()){ 5112 // preload audio JS because of IOS issues 5113 // Sound play() calls are blocked by the OS unless in direct response to a user event like touch or click 5114 // http://www.schillmania.com/projects/soundmanager2/doc/technotes/#mobile-device-limitations 5115 Cargo.Core.Audio.LoadDependancies(null, function(){ 5116 $(window).ready(function(){ 5117 setupSoundManager(); 5118 }); 5119 }); 5120 } 5121 }, 5122 5123 InitButtons : function() { 5124 5125 $(".audio_component .play_pause").off('click').on('click', function(e) { 5126 e.preventDefault(); 5127 5128 var force = false; 5129 if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)) { 5130 var ffversion=new Number(RegExp.$1); 5131 if(ffversion < 3.6) { force = true; } 5132 } 5133 var rel = $(this).attr("rel"); 5134 var which = $(this).attr("class").replace("play_pause ", ""); 5135 5136 // Trigger the event of playing the audio 5137 Cargo.Event.trigger("audio_play_pause", rel, which ); 5138 5139 // Load dependancies, or toggle the sound 5140 if( !Cargo.Core.Audio.Config.has_audio_js ) { 5141 // console.log("load"); 5142 Cargo.Core.Audio.LoadDependancies($(this), function(){ 5143 setupSoundManager(); 5144 }); 5145 } else { 5146 // console.log("toggle"); 5147 Cargo.Core.Audio.ToggleSound($(this)); 5148 } 5149 }); 5150 5151 if($("#DroidFont").length <= 0) { 5152 // Load the external font from the google api 5153 var droidfont=document.createElement('link'); 5154 droidfont.id='DroidFont'; 5155 droidfont.setAttribute('type','text/css'); 5156 droidfont.setAttribute('rel','stylesheet'); 5157 droidfont.setAttribute('href','https://fonts.googleapis.com/css?family=Droid+Sans+Mono'); 5158 5159 var hd=document.getElementsByTagName('head')[0]; 5160 hd.appendChild(droidfont); 5161 }; 5162 5163 $(".progress").css("display","none"); 5164 }, 5165 5166 /** 5167 * Set the player size of the audio component 5168 * Also set the position of each element 5169 */ 5170 InitPlayerSize : function() { 5171 var total_players = $(".audio_component").length; 5172 var zero_count = 0; 5173 5174 $(".audio_component").each(function(){ 5175 if($(this).width() > 0) { 5176 // Setup playhead/progress width 5177 //$('.Href', this).hide(); 5178 var position = $(".position", this); 5179 var playhead = $(".playhead", this); 5180 // This makes the player correctly sized based on the .Sound width property 5181 var soundWidth = $(this).width(); 5182 var soundBordersWidth = $(".border", this).outerWidth() - $(".border", this).width(); 5183 var controlsWidth = $(".controls", this).width(soundWidth - soundBordersWidth).width(); 5184 var volumeWidth = $(".volume", this).width(); 5185 var playpauseWidth = $(".play_pause", this).width(); 5186 var spacerWidths = $(".vertical_border", this).width() * 2; 5187 $(".border", this).width(soundWidth - soundBordersWidth); 5188 $(".info", this).width(controlsWidth - volumeWidth - playpauseWidth - spacerWidths); 5189 5190 var refWidth = $(".info", this).width(); 5191 position.width(refWidth); 5192 5193 playhead 5194 .css("width", refWidth+"px") 5195 .css("left", 0); 5196 5197 var progressBordersWidth = $(".progress", this).outerWidth() - $(".progress", this).width(); 5198 $(".progress") 5199 .css("width", (refWidth - progressBordersWidth) +"px") 5200 .css("left", 0); 5201 5202 if($("#home_content").length > 0) var prog_left = "35px";
5203 else var prog_left = "0px"; 5204 5205 $(".progress_clip") 5206 .css("position", "absolute") 5207 .css("overflow", "hidden") 5208 .css("width", refWidth+"px") 5209 .css("left", prog_left); 5210 5211 $(".position") 5212 .css("left", prog_left); 5213 5214 $(".playhead_container", this) 5215 .css("position", "relative") 5216 .css("width", (playhead.outerWidth() * 2) + "px") 5217 .css("left", ((playhead.outerWidth() * -1)) + "px"); 5218 5219 $(".progress_container", this) 5220 .css("position", "absolute") 5221 .css("z-index", "-1") 5222 .css("width", (playhead.outerWidth() * 2) + "px") 5223 .css("left", ((playhead.outerWidth() * -1)) + "px"); 5224 5225 } // end width check 5226 else { 5227 zero_count++; 5228 } 5229 }); 5230 5231 if( zero_count > 0 && zero_count == total_players) { 5232 // This content is not yet loaded because the container is faded out 5233 // Pause for 1 sec, then enable 5234 setTimeout("Cargo.Core.Audio.InitPlayerSize()", 500); 5235 } 5236 }, 5237 5238 /** 5239 * Load all javascript dependancies 5240 */ 5241 LoadDependancies : function(el, callback) { 5242 // Load audio-related javascript here, but only once 5243 if (this.Config.has_audio_js === false) { 5244 5245 $.loadFiles(this.Config.dependencies).done(function() { 5246 Cargo.Core.Audio.ToggleSound(el); 5247 Cargo.Core.Audio.Config.has_audio_js = true; 5248 if(typeof callback == "function"){ 5249 callback(); 5250 } 5251 }); 5252 5253 return; 5254 } 5255 5256 }, 5257 5258 /** 5259 * When the play/pause button is pressed 5260 */ 5261 ToggleSound : function(el) { 5262 start_sound = $(el).parents(".audio_component").attr("id"); 5263 this_play = $(el); 5264 }, 5265 5266 /** 5267 * Get a count of the div.audio_component elements, confirm if there is more 5268 * than one present 5269 * 5270 * @return Boolean, true if one or more 5271 */ 5272 DetectAudioPlayer: function() { 5273 return ($('.audio_component').length > 0); 5274 }, 5275 5276 /** 5277 * Removes any sound object from the page 5278 */ 5279 UnloadAudio : function() { 5280 $(".audio_component").each(function() { 5281 if(typeof soundManager == "object") { 5282 soundManager.unload( $(this).attr("id") ); 5283 } 5284 }); 5285 } 5286 5287}; 5288 5289 5290 5291 5292/************************************************************************** 5293 * 5294 * Addition to Cargo "core" functionality 5295 * 5296 * The social media class 5297 * This method is a replica of the one found in site.package 5298 * 5299 * Usage: 5300 * Cargo.Core.SocialMedia.Init(); 5301 * 5302 **************************************************************************/ 5303 5304/** 5305 * This is the main social media app 5306 * It will contain the various services 5307 * NOTE: 5308 * The var "Services" sits at the bottom of this method 5309 * this defines each socail network and their dependencies 5310 * it must sit at the bottom, not the top 5311 */ 5312Cargo.Core.SocialMedia = { 5313 5314 /* 5315 * Pre-define the services and their respective scripts that need loading 5316 */ 5317 "Services" : { 5318 "facebook" : { 5319 "script" : false, // script url for inclusion at write time 5320 "inuse" : false // if this is in use or not, always default false 5321 }, 5322 5323 "twitter" : { 5324 "script" : "http://platform.twitter.com/widgets.js", 5325 "inuse" : false 5326 }, 5327 5328 "pinterest" : { 5329 "script" : false, 5330 "inuse" : false 5331 }, 5332 5333 "googleplus" : { 5334 "script" : "https://apis.google.com/js/plusone.js", 5335 "inuse" : false 5336 }, 5337 5338 "stumbleupon": { 5339 "script" : "http://platform.stumbleupon.com/1/widgets.js", 5340 "inuse" : false 5341 }, 5342 5343 "reddit" : { 5344 "script" : "http://www.reddit.com/buttonlite.js?i=5", 5345 "inuse" : false 5346 } 5347 5348 5349 }, 5350 5351 "Options" : { 5352 "element" : ".project_footer", // The selector element to bind the html to 5353 "show_count" : false, // Show the "like" count or not 5354 "twitter_username" : false // Twitter username for @reply 5355 }, 5356 5357 "OutputHTML" : "", // The HTML that will be printed to the element 5358 5359 5360 5361 /* 5362 * Set the defaults of the socail media elements 5363 */ 5364 SetDefaults : function( which, value ) { 5365 if(which == "element" || which == "show_count" || which == "twitter_username") { 5366 this.Options[which] = value; 5367 } else { 5368 // set the "inuse" value for the default 5369 this.Services[which].inuse = value; 5370 } 5371 }, 5372 5373 /* 5374 * Initialize the services 5375 */ 5376 Init : function() { 5377 // Set a delay to listen to the collection loaded 5378 if(Cargo.Helper.IsFeedDesign()) { 5379 Cargo.Event.on('project_collection_reset', function() {
5380 Cargo.Core.SocialMedia.Start(); 5381 }); 5382 } else { 5383 this.Start(); 5384 } 5385 }, 5386 5387 Start : function() { 5388 // Reset the output values 5389 this.OutputHTML = ""; 5390 5391 // Create the HTML 5392 this.CreateHTML(); 5393 5394 // Write the HTML 5395 this.WriteHTML(); 5396 }, 5397 5398 /* 5399 * Gets a project title 5400 */ 5401 AddHTML : function( html ) { 5402 this.OutputHTML += html; 5403 }, 5404 5405 /* 5406 * Takes the HTML that was created by the methods below 5407 * Replaces the stubs with real content 5408 * Stubs are for direct link / encoded direct link / title 5409 * Input is a PID of the element we are binding 5410 * If no PID is passed, it will default to site link / html title 5411 */ 5412 GetFormattedHTML : function( pid ) { 5413 var final_output = this.OutputHTML; 5414 var project_model = Cargo.Collection.Projects.where( { id : pid } )[0]; 5415 5416 project_model = (typeof project_model == "undefined") ? Cargo.Model.Project : project_model; 5417 5418 // Direct link 5419 final_output = final_output.replace(/CARGO_DIRECT_LINK/g, project_model.get('direct_link') ); 5420 5421 // Encoded direct link 5422 final_output = final_output.replace(/CARGO_ENCODED_DIRECT_LINK/g, encodeURIComponent(project_model.get('direct_link')) ); 5423 5424 // Title 5425 final_output = final_output.replace(/CARGO_PROJECT_TITLE/g, project_model.get('title_no_html') ); 5426 5427 // Return the results 5428 return final_output; 5429 }, 5430 5431 /* 5432 * Write the buttons to the choosen element 5433 */ 5434 WriteHTML : function() { 5435 5436 // Attach the content to the choosen element/selector 5437 $(this.Options.element) 5438 .each(function() { 5439 // Add to reclusive elements. Make them social, but skip ones that already are social. 5440 if($(this).data("has_social") !== true) { 5441 $(this) 5442 .append('<div class="social_media">' + Cargo.Core.SocialMedia.GetFormattedHTML( Cargo.Core.SocialMedia.GetNearestPid( $(this) ) ) + '</div>') 5443 .data("has_social", true); 5444 } 5445 }); 5446 5447 5448 // Loop one more time, this time include scripts 5449 $.each(this.Services, function(service, options) { 5450 if( options.inuse === true && options.script !== false ) { 5451 // Remove any old scripts 5452 $("script#"+service).remove(); 5453 5454 // Add the new script 5455 addScript( options.script, service ); 5456 } 5457 }); 5458 5459 }, 5460 5461 /* 5462 * Quick and dirty. 5463 * Finds the pid of the most active element 5464 * On feed design, that is probably it's parent. Find the entry 5465 */ 5466 GetNearestPid : function( this_elem ) { 5467 if(Cargo.Helper.IsFeedDesign()) { 5468 return $(this_elem).closest('[data-id]').attr('data-id'); 5469 } else { 5470 return Cargo.Helper.GetPidFromPurl(Cargo.Helper.GetCurrentProjectUrl()); 5471 } 5472 }, 5473 5474 /* 5475 * Create the final output HTML by adding individual services 5476 */ 5477 CreateHTML : function() { 5478 // Loop through and add each to the selector 5479 for (var service in this.Services) { 5480 // Facebook 5481 if(service == "facebook" && this.Services[service].inuse ) { 5482 this.AddHTML( this.Facebook() ); 5483 } 5484 // Twitter 5485 else if(service == "twitter" && this.Services[service].inuse ) { 5486 this.AddHTML( this.Twitter() ); 5487 } 5488 // Pinterest 5489 else if(service == "pinterest" && this.Services[service].inuse ) { 5490 this.AddHTML( this.Pinterest() ); 5491 } 5492 // Google Plus 5493 else if(service == "googleplus" && this.Services[service].inuse ) { 5494 this.AddHTML( this.GooglePlus() ); 5495 } 5496 // StumbleUpon 5497 else if(service == "stumbleupon" && this.Services[service].inuse ) { 5498 this.AddHTML( this.StumbleUpon() ); 5499 } 5500 // Reddit 5501 else if(service == "reddit" && this.Services[service].inuse ) { 5502 this.AddHTML( this.Reddit() ); 5503 } 5504 5505 } 5506 }, 5507 5508 5509 /* 5510 * All services below here 5511 * Note, string replacements are made for the following: 5512 * CARGO_DIRECT_LINK 5513 * CARGO_ENCODED_DIRECT_LINK 5514 * CARGO_PROJECT_TITLE 5515 */ 5516 Facebook : function() { 5517 5518 // Cargo.log("Should we show comments? "+this.Options.show_count); 5519 5520 return '<div class="facebook_like" style="float: left; margin-right: 8px;"><iframe src="http://www.facebook.com/plugins/like.php?href=CARGO_ENCODED_DIRECT_LINK&send=false&layout=button_count&width=50&show_faces=false&action=like&colorscheme=light&font=arial&height=21&appId=150492405019861" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:46px; height:21px;" allowTransparency="true"></iframe></div>'; 5521 5522 }, 5523 5524 Twitter : function() { 5525 5526 var output = '<div class="tweet_this" style="float: left; margin-right: 8px;"><a href="https://twitter.com/share" class="twitter-share-button"'; 5527 5528 if (this.Options.twitter_username !== false) { 5529 output += ' data-via="'+ this.Options.twitter_username +'"'; 5530 } 5531 5532 output += ' data-url="CARGO_ENCODED_DIRECT_LINK" data-text="CARGO_PROJECT_TITLE: CARGO_DIRECT_LINK" data-count="none">Tweet</a></div>'; 5533 5534 return output; 5535 5536 }, 5537 5538 Pinterest : function() { 5539 5540 $("a.pin_it_link") 5541 .on("click", function() { 5542 addScript("http://assets.pinterest.com/js/pinmarklet.js?r=" + Math.random()*99999999); 5543 return false;
5544 }); 5545 5546 return '<div class="pin_it" style="float: left; margin-right: 8px;"><a href="#" class="pin_it_link" style="background: none; border: 0;"><img src="/_gfx/pinit.png" /></a></div>'; 5547 5548 }, 5549 5550 GooglePlus : function() { 5551 5552 return '<div class="google_plus" style="float: left; margin-right: 8px;"><g:plusone size="medium" annotation="none" href="CARGO_DIRECT_LINK"></g:plusone></div>'; 5553 5554 }, 5555 5556 StumbleUpon : function() { 5557 5558 return '<div class="stumbleupon" style="float: left; margin-right: 8px;"><su:badge layout="4" location ="CARGO_DIRECT_LINK"></su:badge></div>'; 5559 5560 }, 5561 5562 Reddit : function() { 5563 5564 return '<div class="reddit" style="float: left; margin-right: 8px;"><a href="http://www.reddit.com/submit?url=CARGO_ENCODED_DIRECT_LINK" target="_blank"><img src="http://www.reddit.com/static/spreddit5.gif" alt="submit to reddit" border="0" /></a></div>'; 5565 5566 } 5567 5568 5569}; // End SocialMedia 5570 5571Cargo.Core.Inspector3 = { 5572 5573 files: { 5574 corePackage : '/_jsapps/inspector/3.0.7/inspector-core.min.js?02.16.17', 5575 styleSheet : '/_jsapps/inspector/3.0.7/inspector-frontend.min.css?02.16.17', 5576 frontendPackage : '/_jsapps/inspector/3.0.7/inspector-frontend.backbone.min.js?02.16.17' 5577 }, 5578 5579 load: function(customOptions){ 5580 5581 var self = this; 5582 5583 var injectFrontendPackage = function(){ 5584 if(window.frameElement !== null && window.frameElement !== undefined){ 5585 // we're in a frame. That's ok but test if it's 5586 // not a frame that we just created. 5587 5588 if(window.frameElement.getAttribute('data-inspector') !== null){ 5589 5590 // we are in the frame that we've created in this frame's parent. 5591 // No need to do anything. 5592 return false; 5593 } else { 5594 5595 parent.location = document.location.href.replace(/\/adminedit/gi, '') + '?inspector'; 5596 5597 } 5598 } 5599 5600 var script = document.createElement('script'); 5601 script.src = self.files.frontendPackage; 5602 document.head.appendChild(script); 5603 5604 //document.body.style.visibility = 'hidden'; 5605 5606 script.onload = function(){ 5607 5608 setupInspectorFrame(); 5609 5610 } 5611 } 5612 5613 var setupInspectorFrame = function(){ 5614 // only do this when we're not in an iframe. 5615 document.body.setAttribute('style', ''); 5616 document.body.innerHTML = ''; 5617 5618 $(document.body).append('<img class="ci-loader" style="left: 40px; top: 40px; position: absolute;" src="/_gfx/loadingAnim.gif" />'); 5619 5620 if(Cargo.ReplaceLoadingAnims === undefined){ 5621 Cargo.Core.ReplaceLoadingAnims.init(); 5622 } else { 5623 Cargo.ReplaceLoadingAnims(); 5624 } 5625 5626 // remove styleSheets 5627 var styles = document.head.querySelectorAll('link, style'); 5628 5629 for(var i = 0; i < styles.length; i++){ 5630 document.head.removeChild(styles[i]); 5631 } 5632 5633 // add our styleSheet 5634 var style = document.createElement('link'); 5635 style.href = self.files.styleSheet; 5636 style.rel = 'stylesheet'; 5637 document.head.appendChild(style); 5638 5639 5640 var frame = document.createElement('iframe'); 5641 5642 frame.className = 'bodyframe'; 5643 frame.setAttribute('data-inspector', 'true'); 5644 frame.setAttribute('frameborder', '0'); 5645 5646 document.body.appendChild(frame); 5647 5648 $(frame).css('opacity', 0); 5649 5650 // url must be different from the current one to make it work in FF. 5651 var newSrc = window.location.href.replace(/\?inspector|\/admin\/css/gi, ''); 5652 5653 if(newSrc.indexOf('?') === -1){ 5654 newSrc += '?inspecting'; 5655 } else { 5656 newSrc += '&inspecting'; 5657 } 5658 5659 frame.src = newSrc; 5660 5661 // frame loaded. We can access it's DOM now. 5662 frame.onload = function(){ 5663 5664 injectInspectorCore(frame); 5665 5666 //frame.style.visibility = 'hidden'; 5667 } 5668 5669 } 5670 5671 var injectInspectorCore = function(frame){ 5672 // inject the inspector API into the frame 5673 var script = frame.contentDocument.createElement('script'); 5674 script.src = self.files.corePackage; 5675 frame.contentDocument.head.appendChild(script); 5676 5677 // once the API has loaded we can instanciate the editor. 5678 script.onload = function(){ 5679 5680 if(window.editor === undefined){ 5681 initializeInspector(frame); 5682 } else { 5683 window.editor.reloadFrameReferences(); 5684 } 5685 } 5686 } 5687 5688 var initializeInspector = function(frame){ 5689 // instanciate the editor and tell it to inspect the frame instead of the 5690 // current document by passing the iframe object. 5691 5692 $("#follow_popup, .ci-loader").remove(); 5693 5694 var options = $.extend({ 5695 5696 iframe: frame, 5697 5698 styleSheetFilter: function(styleSheet){ 5699 5700 /*if(styleSheet.ownerNode.nodeName === "STYLE"){ 5701 return false;
5702 } 5703 5704 // adblock inserts style nodes with script tags in it... 5705 if(styleSheet.ownerNode !== null){ 5706 if($(styleSheet.ownerNode).find('script').length > 0){ 5707 return false; 5708 } 5709 } 5710 5711 if(styleSheet.href !== null) { 5712 if(styleSheet.href.indexOf('global.css') !== -1){ 5713 return false; 5714 } 5715 }*/ 5716 5717 if(styleSheet.ownerNode !== null){ 5718 if(styleSheet.ownerNode.id.indexOf('member_stylesheet') !== -1 || styleSheet.ownerNode.id.indexOf('user_stylesheet') !== -1 ){ 5719 return true; 5720 } 5721 } 5722 5723 return false; 5724 }, 5725 5726 styleSheetSavableFilter: function(styleSheet){ 5727 5728 if(styleSheet.ownerNode !== null){ 5729 if(styleSheet.ownerNode.id.indexOf('member_stylesheet') !== -1 || styleSheet.ownerNode.id.indexOf('user_stylesheet') !== -1 ){ 5730 return true; 5731 } 5732 } 5733 5734 return false; 5735 }, 5736 5737 onResize: function(){ 5738 $(window).resize(); 5739 }, 5740 5741 onInspectorClick: function(e){ 5742 5743 // don't preventDefault if a splash page has been clicked. 5744 if(e.target.id !== "splash"){ 5745 e.preventDefault(); 5746 } 5747 5748 if( $(e.target).closest('#toolset').length > 0 ) { 5749 return false; 5750 } 5751 5752 if(e.target.nodeName === "HTML") { 5753 e.target = document.body; 5754 } 5755 5756 // return the element that is going to be inspected. 5757 return e.target; 5758 }, 5759 5760 onSave: function(button, modifiedFiles){ 5761 5762 var styleSheetPostArray = []; 5763 5764 var self = this; 5765 5766 for(var i = 0; i < modifiedFiles.length; i++){ 5767 styleSheetPostArray.push({ 5768 filename: encodeURIComponent(modifiedFiles[i].filename), 5769 stylesheet: modifiedFiles[i].cssString, 5770 }); 5771 } 5772 5773 $.ajax({ 5774 type: "POST", 5775 url: '/dispatch/admin/saveInspectorCSS', 5776 data: { 5777 stylesheet: JSON.stringify( styleSheetPostArray ), 5778 url: ( Cargo.Helper ? Cargo.Helper.GetUrl() : frame.contentWindow.Cargo.Location.GetCargoUrl() ) 5779 }, 5780 success: function(response){ 5781 if(response.error.length > 0){ 5782 button.$el.removeClass('saving'); 5783 5784 if(response.error[0].message) { 5785 alert(response.error[0].message); 5786 } else { 5787 alert('Save failed'); 5788 } 5789 } else { 5790 5791 for(var i = 0; i < modifiedFiles.length; i++){ 5792 modifiedFiles[i].CSSStyleSheet.markAsSaved(); 5793 } 5794 5795 } 5796 }, 5797 always: function(){ 5798 button.$el.removeClass('saving'); 5799 }, 5800 dataType: 'json' 5801 }); 5802 5803 } 5804 5805 }, customOptions); 5806 5807 window.editor = new CSSEditor(options); 5808 5809 document.body.appendChild(window.editor.getEditorElement()); 5810 5811 window.editor.getEditorElement().style.setProperty('visibility', 'hidden', 'important'); 5812 5813 var init = true; 5814 5815 $(window).resize(function(){ 5816 5817 var editorWidth = editor.getEditorElement().offsetWidth; 5818 windowWidth = document.documentElement.offsetWidth; 5819 5820 frame.style.width = (windowWidth - editorWidth) + 'px'; 5821 frame.style.left = editorWidth + 'px'; 5822 5823 if(init){ 5824 $(frame).css('opacity', ''); 5825 window.editor.getEditorElement().style.removeProperty('visibility'); 5826 init = false; 5827 } 5828 5829 }); 5830 5831 $(window).resize(); 5832 //frame.style.visibility = ''; 5833 // 5834 var c = window.editor.options.coreWindow.Cargo; 5835 5836 if(c.hasOwnProperty('Event')){ 5837 c.Event.trigger('inspector_open'); 5838 } 5839 5840 } 5841 5842 5843 var adminURLIndex = document.location.href.indexOf('/admin/css'); 5844 5845 if( (Cargo.Helper ? Cargo.Helper.isMobile() : Cargo.Config.isMobile()) ) { 5846 5847 alert('Sorry, the Inspector does not support iOS (yet).'); 5848 5849 if(adminURLIndex !== -1){ 5850 document.location.href = document.location.href.substring(0, adminURLIndex); 5851 } 5852 5853 return false; 5854 5855 } 5856 5857 if( (Cargo.Helper ? Cargo.Helper.isIE8() : Cargo.Config.isIE8()) ) { 5858 5859 alert('Sorry, the Inspector does not support your browser version.'); 5860 5861 if(adminURLIndex !== -1){ 5862 document.location.href = document.location.href.substring(0, adminURLIndex); 5863 } 5864 5865 return false; 5866 5867 } 5868 5869 Cargo.InspectorLoading = true; 5870 $("#follow_popup").remove(); 5871 5872 $.post("/dispatch/admin/authenticateInspector", {url: ( Cargo.Helper ? Cargo.Helper.GetUrl() : Cargo.Location.GetCargoUrl() ) }, function(authenticationData) { 5873 5874 if(authenticationData.error.length === 0) { 5875 5876 injectFrontendPackage(); 5877 5878 } else if(authenticationData.error[0].message) { 5879 alert(authenticationData.error[0].message); 5880 5881 } else { 5882 alert("You must be logged in to use the Inspector."); 5883 5884 if(adminURLIndex !== -1){ 5885 document.location.href = document.location.href.substring(0, adminURLIndex); 5886 } 5887 } 5888 }, 'json'); 5889 5890 //injectFrontendPackage(); 5891 5892 } 5893} 5894 5895Cargo.utils = Cargo.utils || {}; 5896 5897/** 5898 * Cargo.Core.CargoXHR wraps the native XMLHttpRequest with some extra features like retry. 5899 * 5900 * Kinda implements the jQuery (http://api.jquery.com/jquery.ajax/) ajax options. 5901 * 5902 * @author Aart Röst / Cargo Collective 5903 * 5904 */ 5905 5906Cargo.utils.CargoXHR = function(options){ 5907 5908 var self = this; 5909 5910 $.extend(this.options = {}, this.defaultOptions, options); 5911 5912 if(this.options.context === undefined){ 5913 this.options.context = this; 5914 } 5915 5916 if(this.options.url === undefined){ 5917 throw 'no URL given, define "url" in the contructor\'s options object'; 5918 } 5919 5920 this.retries = 0; 5921 5922 this.xhr = new XMLHttpRequest(); 5923 5924 this.xhr.addEventListener("abort", function(){ 5925 self.options.abort.apply(self.options.context, arguments); 5926 }, false); 5927 5928 this.xhr.addEventListener("error", function(){ 5929 self.options.error.apply(self.options.context, arguments); 5930 }, false); 5931 5932 this.xhr.addEventListener("load", function(){ 5933 self.options.success.apply(self.options.context, arguments); 5934 }, false); 5935 5936 this.xhr.addEventListener("loadend", function(){ 5937 self.options.always.apply(self.options.context, arguments); 5938 }, false); 5939 5940 this.xhr.upload.addEventListener("progress", function(e){ 5941 self.options.progress.apply(self.options.context, arguments); 5942 }, false); 5943 5944 this.send(); 5945 5946} 5947 5948Cargo.utils.CargoXHR.prototype.send = function(){ 5949 5950 this.xhr.open(this.options.type, this.options.url, this.options.async); 5951 5952 for(var key in this.options.headers){ 5953 this.xhr.setRequestHeader(key, this.options.headers[key]); 5954 } 5955 5956 this.xhr.responseType = this.options.dataType; 5957
5958 this.options.beforeSend.call(this, this.xhr); 5959 5960 this.xhr.send(this.options.data); 5961 5962} 5963 5964Cargo.utils.CargoXHR.prototype.retry = function(){ 5965 5966 if(this.retries++ < this.options.maxRetries){ 5967 5968 Cargo.utils.CargoXHR.prototype.send.apply(this, arguments); 5969 5970 } else { 5971 5972 this.abort(); 5973 return false; 5974 5975 } 5976 5977} 5978 5979Cargo.utils.CargoXHR.prototype.abort = function(){ 5980 5981 if(typeof this.xhr === 'object'){ 5982 this.xhr.abort(); 5983 } 5984 5985} 5986 5987Cargo.utils.CargoXHR.prototype.defaultOptions = { 5988 async: true, 5989 beforeSend: function(XHR){ 5990 5991 }, 5992 always: function(e){ 5993 5994 }, 5995 success: function(e){ 5996 5997 }, 5998 progress: function(e){ 5999 6000 }, 6001 error: function(e) { 6002 6003 }, 6004 abort: function(e) { 6005 6006 }, 6007 type: 'GET', 6008 url: undefined, 6009 dataType: 'text', 6010 headers: {}, 6011 data: null, 6012 context: undefined, 6013 maxRetries: 5 6014} 6015 6016Cargo.utils = Cargo.utils || {}; 6017 6018/** 6019 * @todo: process fields: totalBytes, uploadedBytes 6020 * global progress percentage calculated with the progress from all processes. 6021 * 6022 */ 6023 6024Cargo.utils.ImageUploader = function(options){ 6025 6026 $.extend(this.options = {}, this.defaultOptions, options); 6027 6028 this.activeProcesses = []; 6029 6030} 6031 6032Cargo.utils.ImageUploader.prototype = { 6033 6034 upload: function(files, pid, customParameters){ 6035 6036 if(files === undefined){ 6037 return undefined; 6038 } 6039 6040 // TODO: handle either a single file or an array of files 6041 if(files.length === 0) { 6042 return undefined; 6043 }; 6044 6045 var _this = this; 6046 6047 // wrap all files in a process. This allows for 6048 // global progress tracking when uploading an array 6049 // of files and easier management of multiple 6050 // concurrent processes. Also allows you to swap out processes 6051 // between uploader instances or re-upload/re-use a certain process. 6052 6053 var uploadProcess = { 6054 uid: Cargo.Model.Site.GetId(), 6055 url: Cargo.Helper.GetUrl(), 6056 pid: (pid !== undefined ? pid : Cargo.Helper.GetCurrentProjectId()), 6057 fileIndex: -1, 6058 aborted: false, 6059 totalBytes: 0, 6060 uploadedBytes: 0, 6061 customParameters: customParameters, 6062 getUploadProgress: function(){ 6063 return (this.uploadedBytes / this.totalBytes) * 100; 6064 }, 6065 files: [], 6066 events: { 6067 onFileComplete: function(file, event){}, 6068 onFileProgress: function(file, event){}, 6069 onFileFail: function(file, event){}, 6070 onFileError: function(file, event){}, 6071 onAbort: function(uploadProcess){}, 6072 onComplete: function(uploadProcess){} 6073 }, 6074 getNextFile: function(){ 6075 6076 if(this.fileIndex < this.files.length - 1 && this.aborted !== true){ 6077 return this.files[++this.fileIndex]; 6078 } 6079 6080 return undefined; 6081 }, 6082 getCurrentFile: function(){ 6083 return this.files[this.fileIndex]; 6084 }, 6085 abort: function(){ 6086 6087 if(this.xhr !== undefined){ 6088 6089 this.aborted = true; 6090 this.xhr.abort(); 6091 this.events.onAbort(this); 6092 6093 var processIndex = _this.activeProcesses.indexOf(this); 6094 6095 if(processIndex !== -1){ 6096 _this.activeProcesses.splice(processIndex, 1); 6097 } 6098 6099 } 6100 6101 }, 6102 addFile: function(file){ 6103 6104 if(this.files.indexOf(file) === -1) { 6105 6106 if(_this.options.acceptFileTypes.test(file.type) && _this.options.acceptFileTypes.test(file.name)){ 6107 6108 uploadProcess.files.push(file); 6109 6110 this.totalBytes += file.size; 6111 6112 } else { 6113 6114 // TODO: Think this through again. 6115 uploadProcess.events.onFileError(file, { 6116 message: 'File type not allowed' 6117 }); 6118 6119 } 6120 6121 } 6122 6123 } 6124 }; 6125 6126 [].forEach.call(files, function(file){ 6127 uploadProcess.addFile(file); 6128 }); 6129 6130 if(uploadProcess.files.length === 0){ 6131 uploadProcess = null; 6132 return undefined; 6133 } 6134 6135 this.activeProcesses.push(uploadProcess); 6136 6137 this.handleUploadProcess(uploadProcess); 6138 6139 return uploadProcess; 6140 6141 }, 6142 6143 handleUploadProcess: function(uploadProcess){ 6144 6145 var file = uploadProcess.getNextFile(), 6146 _this = this; 6147 6148 if(file === undefined) { 6149 6150 // no more files. 6151 uploadProcess.xhr = undefined; 6152 uploadProcess.events.onComplete(uploadProcess); 6153 6154 uploadProcess.fileIndex = -1; 6155 6156 var processIndex = _this.activeProcesses.indexOf(uploadProcess); 6157 6158 if(processIndex !== -1){ 6159 _this.activeProcesses.splice(processIndex, 1); 6160 } 6161 6162 return; 6163 } 6164 6165 file.lastProgress = 0; 6166 6167 var formData = new FormData(); 6168 6169 formData.append('Filedata[]', file); 6170 6171 formData.append('pid', uploadProcess.pid); 6172 formData.append('url', uploadProcess.url); 6173 formData.append('uid', uploadProcess.uid); 6174 6175 if(typeof uploadProcess.customParameters === "object"){ 6176
6177 Object.keys(uploadProcess.customParameters).forEach(function(key){ 6178 6179 formData.append(key, uploadProcess.customParameters[key]); 6180 6181 }); 6182 6183 } 6184 6185 uploadProcess.xhr = new Cargo.utils.CargoXHR({ 6186 6187 url: this.options.posturl, 6188 type: 'POST', 6189 data: formData, 6190 progress: function(e){ 6191 6192 if(e.lengthComputable){ 6193 6194 uploadProcess.uploadedBytes += e.loaded - file.lastProgress; 6195 file.lastProgress = e.loaded; 6196 6197 } 6198 6199 uploadProcess.events.onFileProgress(file, e); 6200 6201 }, 6202 success: function(e){ 6203 6204 if (this.xhr.status == 200) { 6205 6206 var response = JSON.parse(this.xhr.responseText); 6207 uploadProcess.events.onFileComplete.call(this, file, e); 6208 6209 } else { 6210 6211 uploadProcess.events.onFileFail.call(this, file, e); 6212 6213 } 6214 6215 // keep going until we run out of files to upload 6216 _this.handleUploadProcess(uploadProcess); 6217 6218 } 6219 6220 }); 6221 6222 }, 6223 6224 defaultOptions: { 6225 acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, 6226 posturl: '/dispatch/adminproject/handleMultiFileUpload' 6227 } 6228}; 6229 6230 /*************************************************************************** 6231 * 6232 * Helper functions that are not in a class 6233 * 6234 * These are one off functions that do something, but are not tied 6235 * to any particular class 6236 * 6237 **************************************************************************/ 6238 6239/** 6240 * Replication of the php in_array function 6241 * http://phpjs.org/functions/in_array/ 6242 */ 6243function in_array (needle, haystack, argStrict) { 6244 var key = '', 6245 strict = !! argStrict; 6246 6247 if (strict) { 6248 for (key in haystack) { 6249 if (haystack[key] === needle) { 6250 return true; 6251 } 6252 } 6253 } else { 6254 for (key in haystack) { 6255 if (haystack[key] == needle) { 6256 return true; 6257 } 6258 } 6259 } 6260 6261 return false; 6262} 6263 6264/** 6265 * Return the first letter of a word capitolized 6266 */ 6267function ucFirst(str) { 6268 return (str) ? str.charAt(0).toUpperCase() + str.slice(1) : str; 6269} 6270 6271/** 6272 * Legacy function 6273 * CLoses projects 6274 */ 6275function closeThisPr() { 6276 Action.Project.Close(); 6277} 6278 6279/** 6280 * Legacy function 6281 * CLoses projects 6282 */ 6283Projects = { 6284 CloseProject :function() { 6285 Action.Project.Close(); 6286 } 6287} 6288 6289/** 6290 * Legacy function 6291 * Gets a filter 6292 */ 6293Cargo.Location = { 6294 GetUserFilter :function() { 6295 // return Cargo.Model.Site.GetFilter(); 6296 return Cargo.API.Config.filter; 6297 }, 6298 GetDomainOrUrl : function() { 6299 return Cargo.Helper.GetUrl(); 6300 } 6301} 6302 6303/** 6304 * Legacy function 6305 * Loads the inspector 6306 */ 6307function loadInspector() { 6308 Cargo.Core.LoadInspector(); 6309} 6310 6311/** 6312 * Toggles the header into hi-res our out 6313 * Called from the admin frame 6314 */ 6315function ToggleHeaderHiRes(which, url, firstval) { 6316 if($('img[src$="/'+url+'"]').length > 0) { 6317 if(which == "2x") { 6318 if(firstval == "2x") { 6319 $('img[src$="/'+url+'"]').width( $('img[src$="/'+url+'"]').attr('width') ); 6320 $('img[src$="/'+url+'"]').height( $('img[src$="/'+url+'"]').attr('height') ); 6321 } else { 6322 $('img[src$="/'+url+'"]').width( Math.floor($('img[src$="/'+url+'"]').attr('width') / 2) ); 6323 $('img[src$="/'+url+'"]').height( Math.floor($('img[src$="/'+url+'"]').attr('height') / 2) ); 6324 } 6325 6326 } else { 6327 if(firstval == "1x") { 6328 $('img[src$="/'+url+'"]').width( $('img[src$="/'+url+'"]').attr('width') ); 6329 $('img[src$="/'+url+'"]').height( $('img[src$="/'+url+'"]').attr('height') ); 6330 } else { 6331 $('img[src$="/'+url+'"]').width( Math.floor($('img[src$="/'+url+'"]').attr('width') * 2) ); 6332 $('img[src$="/'+url+'"]').height( Math.floor($('img[src$="/'+url+'"]').attr('height') * 2) ); 6333 } 6334 } 6335 } 6336} 6337 6338/** 6339 * Social media class 6340 * 6341 * This is the main social media function 6342 * It will init the rest of the application 6343 * 6344 * Cargo.IncludeSocialMedia( { 6345 * element : ".project_footer", 6346 * show_count : true, 6347 * 6348 * facebook : true, 6349 * twitter : true, 6350 * stumbleupon : false, 6351 * pintrest : true 6352 * } ); 6353 * 6354 * 6355 * .social_media { 6356 * margin: 30px 0 100px 0; height: 20px; 6357 * } 6358 * 6359 */ 6360Cargo.IncludeSocialMedia = function( dataObj ) { 6361 // Loop through each to set the default options 6362 $.each(dataObj, function(key, value) {
6363 Cargo.Core.SocialMedia.SetDefaults(key, value); 6364 }); 6365 6366 // Set the project open/ready states 6367 $(document).ready(function() { 6368 Cargo.Core.SocialMedia.Init(); 6369 }); 6370 6371 Cargo.Event.on('project_load_complete', function( pid ) { 6372 Cargo.Core.SocialMedia.Init(); 6373 }); 6374 6375 Cargo.Event.on('pagination_complete', function( new_page ) { 6376 Cargo.Core.SocialMedia.Init(); 6377 }); 6378 6379}; 6380 6381/** 6382 * Legacy function 6383 * Smothly scroll 6384 */ 6385var lastT = 0; 6386var timer = null; 6387function doscroll(targetYPos,y,t) { 6388 var speed = 6; 6389 var diff = targetYPos - y; 6390 var steps = diff/(speed*speed); 6391 t += (t+steps)/diff; 6392 newY = (t==1) ? y+diff : y + (diff * (-Math.pow(2, -20 * t/1) + 1)); 6393 if(t) window.scrollTo(0, newY); 6394 6395 if(t >= 1 || lastT == t || !t) clearTimeout(timer); 6396 else timer=setTimeout("doscroll("+targetYPos+","+y+","+t+")",1); 6397 6398 lastT = t; 6399 6400 return false; 6401} 6402 6403function doscroll_left(targetXPos,x,t,scroll_obj) { 6404 var speed = 6; 6405 var diff = targetXPos - x; 6406 var steps = diff/(speed*speed); 6407 t += (t+steps)/diff; 6408 newX = (t==1) ? x+diff : x + (diff * (-Math.pow(2, -20 * t/1) + 1)); 6409 if(t) { 6410 if(scroll_obj === window) { 6411 window.scrollTo(newX, 0); 6412 } else { 6413 scroll_obj.scrollLeft = newX; 6414 } 6415 } 6416 6417 if(t >= 1 || lastT == t || !t) clearTimeout(timer); 6418 else timer=setTimeout(function() { 6419 doscroll_left(targetXPos,x,t,scroll_obj); 6420 },1); 6421 6422 lastT = t; 6423 6424 return false; 6425} 6426 6427/** 6428 * Legacy function to add a script into the head 6429 * Used by tracemarks 6430 */ 6431function addScript(script_file, id) { 6432 script_file = (script_file == "/_js/cargo.tracemarklet.js") ? "/_js/cargo.tracemarklet-api.js" : script_file; 6433 var scrpt=document.createElement('script'); 6434 scrpt.setAttribute('type','text/javascript'); 6435 scrpt.setAttribute('src',script_file); 6436 if( id ) scrpt.setAttribute('id',id); 6437 var hd=document.getElementsByTagName('head')[0]; 6438 hd.appendChild(scrpt); 6439} 6440 6441/** 6442 * Legacy function for adding Facebook comments 6443 * 6444 */ 6445///////////////////////////////////////////////////////////////////////////////////////////////// 6446// Facebook comments 6447function fbComments(variant, fbwidth, fbnum){ 6448 addScript("http://connect.facebook.net/en_US/all.js#xfbml=1"); 6449 var f_url = 6450 padding = ""; 6451 var margin = "30px 0 30px 0"; 6452 var colorscheme = (variant == "dark") ? "dark" : "light"; 6453 6454 fbwidth = (!fbwidth) ? 560 : fbwidth; 6455 fbnum = (!fbnum) ? 15 : fbnum; 6456 6457 Cargo.Event.on("project_load_complete", function( pid ) { 6458 fbCommentLoad(pid, variant, fbwidth, fbnum); 6459 }); 6460} 6461///////////////////////////////////////////////////////////////////////////////////////////////// 6462// Load FaceBook comments for non-feed designs 6463function fbCommentLoad(pid, variant, fbwidth, fbnum) { 6464 var margin = "30px 0 30px 0"; 6465 var timeout = 500; 6466 var colorscheme = (variant == "dark") ? "dark" : "light"; 6467 var position = "before"; 6468 var element= $("directlink",Cargo.View.ProjectDetail.$el); 6469 6470 if( !pid ) { 6471 // get the open project 6472 if( $("#current_open").length > 0 && $("#current_open").val() != "none") { 6473 pid = $("#current_open").val(); 6474 6475 } else { 6476 return false; 6477 } 6478 } 6479 6480 // If you're on a project 6481 if(Cargo.Model.Project.GetType() == "project"){ 6482 // Load the Facebook comments script again, for whatever reason(s) 6483 $.getScript("http://connect.facebook.net/en_US/all.js#xfbml=1", function() { 6484 var f_url = Cargo.Model.Project.GetDirectLink(); 6485 6486 if(position == "after") { 6487 $(element).after('<div class="fb_comments" style="width: '+fbwidth+'; margin: '+margin+'; padding: '+padding+';"><fb:comments href="'+f_url+'" width="'+fbwidth+'" num_posts="'+fbnum+'" colorscheme="'+colorscheme+'"></fb:comments></div>'); 6488 } else if(position == "inside") { 6489 $(element).append('<div class="fb_comments" style="width: '+fbwidth+'; margin: '+margin+'; padding: '+padding+';"><fb:comments href="'+f_url+'" width="'+fbwidth+'" num_posts="'+fbnum+'" colorscheme="'+colorscheme+'"></fb:comments></div>'); 6490 } else { 6491 $(element).before('<div class="fb_comments" style="width: '+fbwidth+'; margin: '+margin+'; padding: '+padding+';"><fb:comments href="'+f_url+'" width="'+fbwidth+'" num_posts="'+fbnum+'" colorscheme="'+colorscheme+'"></fb:comments></div>'); 6492 } 6493 6494 // Force FB to re-parse the comments 6495 FB.XFBML.parse(); 6496 6497 }); 6498 } 6499} 6500 6501 6502 Cargo.o.model.project = Backbone.Model.extend({ 6503 GetId : function() { 6504 return this.get("id"); 6505 }, 6506 6507 GetSiteId : function() { 6508 return this.get("site_id"); 6509 }, 6510 6511 GetUrl : function() { 6512 return this.get("url"); 6513 }, 6514 6515 GetProjectUrl : function() { 6516 return this.get("project_url"); 6517 }, 6518 6519 GetDirectLink : function() { 6520 return this.get("direct_link"); 6521 }, 6522 6523 GetType : function() { 6524 return (this.get("type") && this.get("id") != 0) ? this.get("type") : false; 6525 }, 6526 6527 GetSetId : function() { 6528 return this.get("set_id"); 6529 }, 6530 6531 GetSetName : function() { 6532 return this.get("set_name"); 6533 }, 6534 6535 GetSetUrl : function() { 6536 return this.get("set_url"); 6537 }, 6538 6539 GetSetCount : function() { 6540 return this.get("set_count"); 6541 }, 6542 6543 GetSetStart : function() { 6544 return this.get("set_start"); 6545 }, 6546 6547 GetSetEnd : function() { 6548 return this.get("set_end"); 6549 }, 6550 6551 GetTitle : function() { 6552 return this.get("title"); 6553 }, 6554 6555 GetTitleNoHtml : function() { 6556 return this.get("title_no_html"); 6557 }, 6558 6559 GetShowTitle : function() { 6560 return this.get("show_title"); 6561 }, 6562 6563 GetTags : function() { 6564 return this.get("tags"); 6565 }, 6566 6567 GetDisplay : function() { 6568 return this.get("display"); 6569 }, 6570 6571 GetExcerpt : function() { 6572 return this.get("excerpt"); 6573 }, 6574 6575 GetContent : function() { 6576 return this.get("content"); 6577 }, 6578 6579 GetContent2x : function() { 6580 return this.get("content_2x"); 6581 }, 6582 6583 GetContentNoHtml : function() { 6584 return this.get("content_no_html"); 6585 }, 6586 6587 GetContentPartialHtml : function() { 6588 return this.get("content_partial_html"); 6589 }, 6590 6591 GetViews : function() { 6592 return this.get("views"); 6593 }, 6594 6595 GetDate : function() { 6596 return this.get("date"); 6597 }, 6598 6599 GetBgColor : function() { 6600 return (this.get("bgcolor") == "") ? false : "#"+this.get("bgcolor"); 6601 }, 6602 6603 GetCommentCount : function() { 6604 return this.get("comment_count"); 6605 }, 6606 6607 GetThumbUrl : function() { 6608 return this.get("thumb_url"); 6609 }, 6610 6611 GetThumbUrl2x : function() { 6612 return this.get("thumb_url_2x"); 6613 }, 6614 6615 GetThumbWidth : function() { 6616 return this.get("thumb_width"); 6617 }, 6618 6619 GetThumbHeight : function() { 6620 return this.get("thumb_height"); 6621 }, 6622 6623 GetDefaultThumbUrl : function() { 6624 return this.get("default_thumb_url"); 6625 }, 6626 6627 GetDefaultThumb2xUrl : function() { 6628 return this.get("default_thumb_url_2x"); 6629 }, 6630 6631 GetDefaultThumbWidth : function() { 6632 return this.get("default_thumb_width"); 6633 }, 6634 6635 GetDefaultThumbHeight : function() { 6636 return this.get("default_thumb_height"); 6637 }, 6638 6639 GetCustomThumbUrl : function() { 6640 return this.get("custom_thumb_url"); 6641 }, 6642 6643 GetCustomThumb2xUrl : function() { 6644 return this.get("custom_thumb_url_2x"); 6645 }, 6646 6647 GetCustomThumbWidth : function() { 6648 return this.get("custom_thumb_width"); 6649 }, 6650 6651 GetCustomThumbHeight : function() { 6652 return this.get("custom_thumb_height"); 6653 }, 6654 6655 GetImages : function() { 6656 return this.get("images"); 6657 }, 6658 6659 initialize : function() { 6660 //Cargo.Core.log(this); 6661 6662 }, 6663 6664 fetch : function(options) { 6665 if ( options.reset && $("script[data-set='ProjectDetail']").html() ) { 6666 this.set( $.parseJSON( $("script[data-set='ProjectDetail']").html() ), options ); 6667 6668 } else { 6669 Backbone.Model.prototype.fetch.apply(this, arguments); 6670 } 6671 }, 6672 6673 /** 6674 * Update the parent collection model on parse (save response) 6675 */ 6676 parse: function(resp, options) { 6677 var project_model = Cargo.Collection.Projects.findWhere( { project_url : this.GetProjectUrl() } ); 6678 var page_model = Cargo.Collection.Pages.findWhere( { project_url : this.GetProjectUrl() } ); 6679 var model = (page_model) ? page_model : project_model; 6680 if(model) model.set(resp); 6681 return resp; 6682 }, 6683 6684 url : function() { 6685 return Cargo.API.GetSingleProjectPath(); 6686 } 6687 6688 }); 6689 6690 6691 6692/** 6693 * Default model for constructing display options 6694 * Because these are nessesary for first load of project collection 6695 * This data may be preloaded on the page. For this reason, 6696 * we extend the fetch function to check to see if this is the case 6697 * If it is not the case, then fetch from the API url 6698 * 6699 * Usage: 6700 * <script type="text/json" data-set="DisplayOptions">{options}</script> 6701 */ 6702 6703 Cargo.o.model.display_options = Backbone.Model.extend({ 6704 6705 fetch : function(options) { 6706 if ( $("script[data-set='DisplayOptions']").html() ) { 6707 this.set( $.parseJSON( $("script[data-set='DisplayOptions']").html() ), options ); 6708 6709 } else { 6710 Backbone.Model.prototype.fetch.apply(this, arguments); 6711 } 6712 }, 6713 6714 url : function() { 6715 return Cargo.API.GetDisplayOptionsPath(); 6716 }, 6717 6718 6719 GetUserId : function() { 6720 return this.get("user_id"); 6721 }, 6722 6723 GetPaginationCount : function() { 6724 return this.get("pagination_count"); 6725 }, 6726 6727 GetTotalProjects : function() { 6728 /* Get the total amount of projects, which may be filtered */ 6729 var total = (Cargo.API.Config.filter_total) ? Math.floor(Cargo.API.Config.filter_total) : Math.floor(this.get("total_projects")); 6730 return total; 6731 }, 6732 6733 GetFollowLinkInNav : function() { 6734 return this.get("following_link_in_nav"); 6735 }, 6736 6737 GetDisableProjectScroll : function() { 6738 return this.get("disable_project_scroll"); 6739 }, 6740 6741 GetHeaderLinkToIndex : function() { 6742 return this.get("header_link_to_index"); 6743 }, 6744 6745 GetHeader2x : function() { 6746 return this.get("header_2x"); 6747 }, 6748 6749 GetTagsInThumb : function() { 6750 return this.get("tags_in_thumb"); 6751 }, 6752 6753 GetTitleInThumb : function() { 6754 return this.get("title_in_thumb"); 6755 }, 6756 6757 GetThumbSize : function() { 6758 return this.get("thumb_size"); 6759 }, 6760 6761 GetThumbsBelowProjects : function() { 6762 return this.get("thumbs_below_projects"); 6763 }, 6764 6765 GetThumbsBelowPages : function() { 6766 return this.get("thumbs_below_pages"); 6767 }, 6768 6769 GetViewCountInProject : function() { 6770 return this.get("view_count_in_project"); 6771 }, 6772 6773 GetTitleInProject : function() { 6774 return this.get("title_in_project"); 6775 }, 6776 6777 GetFooterInProject : function() { 6778 return this.get("footer_in_project"); 6779 }, 6780 6781 GetPagesInTextNav : function() { 6782 return this.get("pages_in_text_nav"); 6783 }, 6784 6785 GetProjectsInTextNav : function() { 6786 return this.get("projects_in_text_nav"); 6787 }, 6788 6789 GetUseSets : function() { 6790 return this.get("use_sets"); 6791 }, 6792 6793 GetSetsOpenByDefault : function() { 6794 return this.get("sets_open_by_default"); 6795 }, 6796 6797 GetSetsCanToggle : function() { 6798 return this.get("sets_can_toggle"); 6799 }, 6800 6801 GetSetsOneAtATime : function() { 6802 return this.get("sets_one_at_a_time"); 6803 }, 6804 6805 GetUseSetLinks : function() { 6806 return this.get("use_set_links"); 6807 }, 6808 6809 GetSetLinksPosition : function() { 6810 return this.get("set_links_position"); 6811 }, 6812 6813 GetRecentProjectAsStart : function() { 6814 return this.get("recent_project_as_start"); 6815 }, 6816 6817 GetExcerptInThumb : function() { 6818 return this.get("excerpt_in_thumb"); 6819 }, 6820 6821 GetLightboxView : function() { 6822 return this.get("lightbox_view"); 6823 }, 6824 6825 GetImageWidth : function() { 6826 return this.get("image_width"); 6827 }, 6828 6829 // Slideshows 6830 GetSlideTransition : function() { 6831 return this.get("slide_transition"); 6832 }, 6833 6834 GetSlideTransitionDuration : function() { 6835 return this.get("slide_transition_duration"); 6836 }, 6837 6838 GetSlideAutoPlay : function() { 6839 return this.get("slide_auto_play"); 6840 }, 6841 6842 GetSlideTextNav : function() { 6843 return this.get("slide_text_nav"); 6844 }, 6845 6846 GetSlideResponsive : function() { 6847 return this.get("slideshow_responsive"); 6848 }, 6849 6850 GetSlideImagePosition : function() { 6851 return this.get("slide_image_position"); 6852 }, 6853 6854 GetSlideNavPosition : function() { 6855 return this.get("slide_nav_position"); 6856 }, 6857 6858 GetSlideThumbnails : function() { 6859 return this.get("slide_thumbnails"); 6860 }, 6861 6862 GetSlideDefaultThumbs : function() { 6863 return this.get("slide_default_thumbs"); 6864 }, 6865 6866 GetSlideToggleThumbLink : function() { 6867 return this.get("slide_toggle_thumb_link"); 6868 }, 6869 6870 GetIndexRouteUrl : function() { 6871 return this.get("index_route_url"); 6872 }, 6873 6874 GetIndexRouteName : function() { 6875 return this.get("index_route_name"); 6876 }, 6877 6878 GetFeedRouteUrl : function() { 6879 return this.get("feed_route_url"); 6880 }, 6881 6882 GetFeedRouteName : function() { 6883 return this.get("feed_route_name"); 6884 }, 6885 6886 GetHomeRoute : function() { 6887 return this.get("home_route"); 6888 } 6889 6890 6891 }); 6892 6893 6894 6895 6896 Cargo.o.model.site = Backbone.Model.extend({ 6897 6898 fetch : function(options) { 6899 if ( $("script[data-set='Site']").html() ) { 6900 this.set( $.parseJSON( $("script[data-set='Site']").html() ), options ); 6901 6902 } else { 6903 Backbone.Model.prototype.fetch.apply(this, arguments); 6904 } 6905 }, 6906 6907 url : function() { 6908 var path = Cargo.API.GetSitePath(); 6909 6910 if( Cargo.API.Config.filter != "" ) { 6911 path += "?filter=" + encodeURIComponent(Cargo.API.Config.filter); 6912 } 6913 return path; 6914 }, 6915 6916 GetId : function() { 6917 return this.get("id"); 6918 }, 6919 6920 GetDirectLink : function() { 6921 return this.get("direct_link"); 6922 }, 6923 6924 GetDisplayUrl : function() { 6925 return this.get("display_url"); 6926 }, 6927 6928 GetAssetPath : function(asset, id) { 6929 if(asset && id) { 6930 return this.get("asset_path") + "/" + asset + "?" + id; 6931 } else { 6932 return this.get("asset_path"); 6933 } 6934 }, 6935 6936 GetWebsiteTitle : function() { 6937 return this.get("website_title"); 6938 }, 6939 6940 GetMetaTags : function() { 6941 return this.get("meta_tags"); 6942 }, 6943 6944 GetMetaDescription : function() { 6945 return this.get("meta_description"); 6946 }, 6947 6948 GetMetaHead : function() { 6949 return this.get("meta_head"); 6950 }, 6951 6952 GetStartProjectId : function() { 6953 return this.get("start_project_id"); 6954 }, 6955 6956 GetThumbUrl : function() { 6957 return this.get("thumb_url"); 6958 }, 6959 6960 GetCssUrl : function() { 6961 return this.get("css_url"); 6962 }, 6963 6964 GetFaviconUrl : function() { 6965 return this.get("favicon_url"); 6966 }, 6967 6968 GetHeaderUrl : function() { 6969 return this.get("header_url"); 6970 }, 6971 6972 GetHeaderWidth : function() { 6973 return this.get("header_width"); 6974 }, 6975 6976 GetHeaderHeight : function() { 6977 return this.get("header_height"); 6978 }, 6979 6980 GetFilter : function() { 6981 return (this.get("filter")) ? this.get("filter") : Cargo.API.Config.filter; 6982 }, 6983 6984 GetIsEditor : function() { 6985 return this.get("is_editor"); 6986 }, 6987 6988 GetUseHiRes : function() { 6989 return this.get("use_hi_res"); 6990 }, 6991 6992 GetDisplayOption : function( which ) { 6993 if(typeof this.get("display_options") != "undefined" ) { 6994 return this.get("display_options")[which]; 6995 } else { 6996 return false;
6997 } 6998 6999 }, 7000 7001 GetFileUrl : function( which ) { 7002 if(typeof this.get("files") != "undefined" ) { 7003 return this.get("files")[which]; 7004 } else { 7005 return false; 7006 } 7007 7008 }, 7009 7010 GetCurrentSetKey : function() { 7011 return this.get("current_set_key"); 7012 }, 7013 7014 GetResourceUrl : function() { 7015 return this.get("resource_url"); 7016 }, 7017 7018 /** 7019 * Overwrite the parse function because the api 7020 * can not return the proper cookie value for editor 7021 */ 7022 parse : function (response) { 7023 if( !Cargo.Helper.IsAdminEdit() ) { 7024 response.is_editor = Cargo.Helper.GetIsEditor(); 7025 } 7026 return response; 7027 } 7028 7029 7030 }); 7031 7032 7033/** 7034 * Default model for constructing the Rail content 7035 */ 7036 7037 Cargo.o.model.rail = Backbone.Model.extend({ 7038 7039 url : function() { 7040 return Cargo.API.GetRailPath(); 7041 }, 7042 7043 GetTemplates : function() { 7044 return this.get('templates'); 7045 }, 7046 7047 GetModels : function() { 7048 return this.get('models'); 7049 }, 7050 7051 GetPopup : function() { 7052 return this.get('jdata')['popup']; 7053 } 7054 7055 7056 }); 7057 7058 7059 7060/** 7061 * Default model for assets 7062 */ 7063 7064Cargo.o.model.asset = Backbone.Model.extend({ 7065 7066}); 7067 7068 7069 7070/** 7071 * Default model for images 7072 */ 7073 7074Cargo.o.model.images = Backbone.Model.extend({ 7075 7076}); 7077 7078 7079 7080 7081/** 7082 * Default undefined model 7083 */ 7084 7085Cargo.o.model.undefined_default = Backbone.Model.extend({ 7086 7087}); 7088 7089 7090 7091 7092 Cargo.o.collection.projects = Backbone.Collection.extend({ 7093 7094 initialize : function() { 7095 7096 }, 7097 7098 url : function() { 7099 return Cargo.API.GetProjectsPath(); 7100 } 7101 7102 }); 7103 7104 7105 7106 Cargo.o.collection.pages = Backbone.Collection.extend({ 7107 7108 initialize : function() { 7109 7110 }, 7111 7112 url : function() { 7113 return Cargo.API.GetProjectsPath(null, null, 'page'); 7114 } 7115 7116 }); 7117 7118 7119 7120 Cargo.o.collection.navigation = Backbone.Collection.extend({ 7121 7122 initialize : function() { 7123 7124 }, 7125 7126 url : function() { 7127 return Cargo.API.GetNavigationCargoPath(); 7128 } 7129 7130 }); 7131 7132 7133 7134 Cargo.o.collection.assets = Backbone.Collection.extend({ 7135 7136 initialize : function() { 7137 7138 }, 7139 7140 fetch : function(options) { 7141 if ( $("script[data-set='Assets']").html() ) { 7142 this.set( $.parseJSON( $("script[data-set='Assets']").html() ), options ); 7143 7144 } 7145 } 7146 7147 }); 7148 7149 7150 7151 Cargo.o.collection.undefined_default = Backbone.Collection.extend({ 7152 7153 initialize : function() { 7154 7155 }, 7156 7157 url : function() { 7158 7159 } 7160 7161 }); 7162 7163 7164 7165 Cargo.o.collection.sets = Backbone.Collection.extend({ 7166 7167 initialize : function() { 7168 7169 }, 7170 7171 url : function() { 7172 return Cargo.API.GetSetsPath(0, 9999); 7173 } 7174 7175 }); 7176 7177 7178 7179 Cargo.o.collection.images = Backbone.Collection.extend({ 7180 7181 initialize : function() { 7182 7183 }, 7184 7185 url : function() { 7186 var offset = this.options.offset; 7187 var limit = this.options.limit; 7188 var sort = this.options.sort; 7189 return Cargo.API.GetImagesPath(offset, limit, sort); 7190 }, 7191 7192 fetch : function(options) { 7193 this.options = options; 7194 Backbone.Model.prototype.fetch.apply(this, arguments); 7195 } 7196 7197 }); 7198 7199 7200 7201 Cargo.o.collection.search = Backbone.Collection.extend({ 7202 7203 initialize : function() { 7204 7205 }, 7206 7207 url : function() { 7208 var term = (this.options && this.options.term) ? this.options.term : false; 7209 return Cargo.API.GetSearchPath(term); 7210 }, 7211 7212 fetch : function(options) { 7213 this.options = options; 7214 Backbone.Model.prototype.fetch.apply(this, arguments); 7215 } 7216 7217 }); 7218 7219 7220 Cargo.o.view.Autopaginate = Backbone.View.extend({ 7221 7222 initialize: function() { 7223 this.Data = { 7224 "defered_count" : 0, // count how many defered events have been fired 7225 "is_updating" : false, // are we currently waiting on ajax? 7226 "within_bounds" : false, // within the bounds of paginating? 7227 "preload_distance" : 500, // Distance before the end of the page to fire 7228 "model_count" : 0, // How many models do we have 7229 "direction" : 'vertical' // Which direction to check distance - vertical || horizontal 7230 }; 7231 7232 var self = this; 7233 7234 self.tallProjectFix = Cargo.Helper.IsFeedDesign() && $('[data-view="Autopaginate"]').length == 1; 7235 7236 /* Set an event to wait for the model to change */ 7237 this.listenTo(this.collection, "reset", this.reset); 7238 this.listenTo(this.collection, "add", this.add); 7239 7240 /* Loop through the data attributes and update the defaults */ 7241 $.each(this.$el.data(), function(i, v) { 7242 self.Data[i] = v; 7243 }); 7244 7245 /* Set the element for the scroll container and the height continer */ 7246 this.Data.scroll_el = (this.Data.container) ? $($("#"+this.Data.container).parents('div')[0]) : $(document); 7247 this.Data.size_el = (this.Data.container) ? $("#"+this.Data.container) : $(document); 7248 7249 this.Data.direction = ($(this.$el).data('direction')) ? $(this.$el).data('direction') : this.Data.direction; 7250 7251 7252 /* Bind the scroll */ 7253 _.delay(function() { 7254 self.Data.scroll_el.bind("scroll", function(event) { 7255 _.delay(function() { 7256 self.CheckForPagination(); 7257 }, 100); 7258 }); 7259 }, 200); 7260 7261 /* Defer the toggle of the project until after the collection is reset */ 7262 Cargo.Event.on("project_collection_reset", function( ) { 7263 /* Tell the updater that we are done */ 7264 self.Data.is_updating = false;
7265 7266 /* Hide the spinner */ 7267 self.$el.hide(); 7268 7269 }); 7270 7271 /* Defer the checking of the first load pagination until after collection and template */ 7272 Cargo.Event.on("project_collection_reset template_loaded", function(template) { 7273 /* Check to see if we have enough page 7274 * height to fire pagination right now 7275 * but only do this on first load 7276 * and only after the project collection is reset 7277 * and only after the thumbnail template has been loaded */ 7278 if( template == "Thumbnail") { 7279 _.delay(function() { 7280 self.CheckForPagination(); 7281 }, 100); 7282 } 7283 }); 7284 7285 $(function(){ 7286 self.last_project = $('.project_container').last(); 7287 self.CheckForPagination(); 7288 }); 7289 }, 7290 7291 reset : function() { 7292 Cargo.Event.trigger("pagination_complete", Cargo.API.Config.current_page ); 7293 this.last_project = $('.project_container').last(); 7294 }, 7295 7296 add : function() { 7297 this.Data.model_count++; 7298 7299 // Make sure we have added all the models before firing the complete event 7300 if(this.collection.length == this.Data.model_count) { 7301 Cargo.Event.trigger("pagination_complete", Cargo.API.Config.current_page ); 7302 this.last_project = $('.project_container').last(); 7303 7304 /* Tell the updater that we are done */ 7305 this.Data.is_updating = false; 7306 } 7307 }, 7308 7309 7310 CheckForPagination : function() { 7311 if(this.Data.direction == "horizontal") { 7312 this.CheckPageWidth(); 7313 } else { 7314 this.CheckPageHeight(); 7315 } 7316 }, 7317 7318 /* 7319 * Checks if we are within bounds of paginating 7320 */ 7321 CheckPageHeight : function() { 7322 // Don't do this if we're editing a project 7323 if( Cargo.Helper.IsAdminEditProject() ) { 7324 // Do nothing 7325 return false; 7326 } 7327 7328 var last_project_height = 0, 7329 window_height = $(window).height(), 7330 window_scroll = this.Data.scroll_el.scrollTop() + window_height, 7331 document_size = this.Data.size_el.height()-this.Data.preload_distance; 7332 7333 if(this.last_project && this.last_project.length > 0 && this.tallProjectFix){ 7334 last_project_height = this.last_project.height(); 7335 if(last_project_height > window_height - 10) { 7336 document_size -= (last_project_height - window_height); 7337 } 7338 } 7339 7340 if( window_scroll > document_size ) { 7341 this.Data.within_bounds = true; 7342 } else { 7343 this.Data.within_bounds = false; 7344 } 7345 7346 /* Make sure there are more pages and the container is visible */ 7347 if( 7348 Cargo.Helper.GetTotalPages() > Cargo.API.Config.current_page && 7349 ( 7350 ( typeof Cargo.View.Thumbnail != "undefined" && Cargo.View.Thumbnail.$el.is(":visible") ) || 7351 typeof Cargo.View.Thumbnail == "undefined" 7352 ) && 7353 !Cargo.Helper.IsSololink() 7354 7355 ) { 7356 if (this.Data.within_bounds && !this.Data.is_updating) { 7357 Cargo.Event.trigger("pagination_start"); 7358 this.Data.is_updating = true; 7359 this.Data.within_bounds = false; 7360 this.$el.show(); 7361 Cargo.View.Main.NextPage(); 7362 } 7363 } 7364 7365 }, 7366 7367 /* 7368 * Checks if we are within bounds of paginating 7369 */ 7370 CheckPageWidth : function() { 7371 // Don't do this if we're editing a project 7372 if( Cargo.Helper.IsAdminEditProject() ) { 7373 // Do nothing 7374 return false; 7375 } 7376 7377 var last_project_width = 0, 7378 window_width = $(window).width(), 7379 window_scroll = this.Data.scroll_el.scrollLeft() + window_width, 7380 document_size = this.Data.size_el.width()-this.Data.preload_distance; 7381 7382 if(this.last_project.length > 0 && this.tallProjectFix){ 7383 last_project_width = this.last_project.width(); 7384 if(last_project_width > window_width - 10) { 7385 document_size -= (last_project_width - window_width); 7386 } 7387 } 7388 7389 if( window_scroll > document_size ) { 7390 this.Data.within_bounds = true; 7391 } else { 7392 this.Data.within_bounds = false; 7393 } 7394 7395 /* Make sure there are more pages and the container is visible */ 7396 if( 7397 Cargo.Helper.GetTotalPages() > Cargo.API.Config.current_page && 7398 ( 7399 ( typeof Cargo.View.Thumbnail != "undefined" && Cargo.View.Thumbnail.$el.is(":visible") ) || 7400 typeof Cargo.View.Thumbnail == "undefined" 7401 ) && 7402 !Cargo.Helper.IsSololink() 7403 7404 ) { 7405 if (this.Data.within_bounds && !this.Data.is_updating) { 7406 Cargo.Event.trigger("pagination_start"); 7407 this.Data.is_updating = true; 7408 this.Data.within_bounds = false;
7409 this.$el.show(); 7410 Cargo.View.Main.NextPage(); 7411 } 7412 } 7413 7414 } 7415 7416 }); 7417 7418/** 7419 * This view handles the main website's view 7420 * It binds to the body element and is loaded only once on page load 7421 * This is mostly to handle the global click events for history and close 7422 * However, maybe it will handle other things as well 7423 * If we would render the entire app from JS, then this would render the init design 7424 */ 7425 7426 Cargo.o.view.Main = Backbone.View.extend({ 7427 el: 'body', 7428 7429 initialize: function() { 7430 // Set an event to wait for the model to change 7431 this.collection.bind("change", this.render, this); 7432 7433 /* 7434 * If there is a custom HTML/CSS preview 7435 */ 7436 if( Cargo.API.Config.preview == "html" ) { 7437 this.WriteCustomHtmlPreview(); 7438 } else if( Cargo.API.Config.preview == "css" ) { 7439 this.WriteCustomCSSPreview(); 7440 } 7441 7442 /* 7443 * Init the Rail and Following, but not in admin or mobile mode 7444 */ 7445 if( !Cargo.Helper.IsAdminEdit() && typeof Mobile != "object") { 7446 Cargo.View.Rail = new Cargo.o.view.Rail({ 7447 el : "body", 7448 collection : Cargo.Model.Rail 7449 }); 7450 7451 Cargo.View.Following = new Cargo.o.view.Following({ el : "body" }); 7452 } 7453 7454 }, 7455 7456 events: { 7457 "click [rel='history']" : "HistoryPush", 7458 "click [rel='show_index']" : "ShowIndex", 7459 "click [rel='close_project']" : "ShowIndex", // API 0.1 legacy support 7460 "click [rel='show_home']" : "ShowHome", 7461 "click [rel='next_project']" : "NextProject", 7462 "click [rel='prev_project']" : "PrevProject", 7463 "click [rel='random_project']" : "RandomProject", 7464 "click [rel='next_page']" : "NextPage", 7465 "click [rel='prev_page']" : "PrevPage" 7466 }, 7467 7468 /** 7469 * Site model has loaded, init from here 7470 */ 7471 render : function() { 7472 var model = this.collection; 7473 }, 7474 7475 /* 7476 * Pushes a history item onto the stack by navigating to 7477 * the url found in the href property 7478 * Triggers the global event history add_history 7479 * Works with any tag 7480 * Usage: 7481 * <div data-href="/some/link" rel="history">Click me</div> 7482 */ 7483 HistoryPush : function( e, target ) { 7484 if( e ) { 7485 /* Remove default action */ 7486 e.preventDefault(); 7487 target = e.currentTarget; 7488 } 7489 7490 /* Get the absolute anchor href */ 7491 var href = ( $(target).attr("href") ) ? $(target).attr("href") : $(target).data("href"); 7492 var id = ( $(target).attr("data-id") ) ? $(target).attr("data-id") : $(target).data("id"); 7493 7494 /* ctrl+click */ 7495 if (e.metaKey) { 7496 window.open( href ); 7497 } else { 7498 /* Navigate to the new URL */ 7499 Cargo.Event.trigger("add_history", href, id); 7500 } 7501 }, 7502 7503 /* 7504 * Closes an open project by navigating to the homepage 7505 * Works with any tag 7506 * Usage: 7507 * <a href="#" rel="show_index">Close</a> 7508 * Or 7509 * Cargo.View.Main.ShowIndex(); 7510 */ 7511 ShowIndex : function( e ) { 7512 if( e ) { 7513 /* Remove default action (if there is one) */ 7514 e.preventDefault(); 7515 } 7516 7517 /* If this is a feed based design, then there will be an index_route 7518 * if that is the case, go there */ 7519 if( Cargo.Model.DisplayOptions.GetIndexRouteName() ) { 7520 Cargo.Helper.GoToUrl( Cargo.Model.DisplayOptions.GetIndexRouteUrl() ); 7521 return; 7522 } 7523 7524 /* If this project is the start project, close passively 7525 * Otherwise, close normally */ 7526 if( Cargo.API.Config.start_project ) { 7527 Cargo.Event.trigger("add_history_passive", "" ); 7528 7529 Cargo.API.Config.start_project = null; 7530 7531 /* Update the data model to remove the last project */ 7532 Cargo.Model.Project.set( { id : 0, project_url : null } ); 7533 7534 7535 } else { 7536 /* Navigate to the Home route */ 7537 var home_route = ""; //(Cargo.API.Config.set) ? Cargo.API.Config.set : ""; 7538 Cargo.Event.trigger("add_history", home_route ); 7539 } 7540 7541 }, 7542 7543 /** 7544 * Closes an open project, navigates to the root 7545 * and re-opens the start page 7546 * Usage: 7547 * <a href="#" rel="show_home">Home</a> 7548 * Or 7549 * Cargo.View.Main.ShowHome(); 7550 */ 7551 ShowHome : function(e) { 7552 if( e ) { 7553 /* Remove default action (if there is one) */ 7554 e.preventDefault(); 7555 } 7556 7557 /* If this is a feed based design, then there will be an index_route 7558 * if that is the case, go there */ 7559 if( Cargo.Model.DisplayOptions.GetIndexRouteName() ) { 7560 Cargo.Helper.GoToUrl( Cargo.Model.DisplayOptions.GetIndexRouteUrl() ); 7561 return; 7562 } 7563 7564 /* If this project is the start project, close passively 7565 * Otherwise, close normally */ 7566 if( Cargo.API.Config.start_project ) { 7567 Cargo.Event.trigger("add_history_passive", "" ); 7568 7569 /* 7570 * Try and get the model from memory first 7571 */ 7572 var project_model = Cargo.Collection.Projects.findWhere( { id : Cargo.Helper.GetStartProjectId() } ); 7573 var page_model = Cargo.Collection.Pages.findWhere( { id : Cargo.Helper.GetStartProjectId() } ); 7574 var home_model = (typeof Persona.Model.HomeProject == "object") ? Persona.Model.HomeProject : false;
7575 var model = (page_model) ? page_model : (home_model) ? home_model : project_model; 7576 7577 if(model) { 7578 Cargo.Model.Project.set( model.toJSON() ); 7579 7580 } else { 7581 // If there is no model, then load one async 7582 Cargo.Model.Project.fetch( { data : { pid : Cargo.Helper.GetStartProjectId() }} ); 7583 } 7584 7585 7586 } else { 7587 /* Navigate to the Home route */ 7588 var home_route = ""; //(Cargo.API.Config.set) ? Cargo.API.Config.set : ""; 7589 Cargo.Event.trigger("add_history", home_route ); 7590 } 7591 }, 7592 7593 /** 7594 * Next project 7595 * If we are on the last project of a page, go to the next page 7596 * If we are on the last project of the last page, go to page 1 7597 * Works with any tag 7598 * Usage: 7599 * <a href="#" rel="next_project">Next project</a> 7600 * Or 7601 * Cargo.View.Main.NextProject(); 7602 */ 7603 NextProject : function( e, dont_check ) { 7604 if( e ) { 7605 /* Remove default action (if there is one) */ 7606 e.preventDefault(); 7607 } 7608 7609 /* 7610 * If the next project needs to paginated 7611 * then paginate and delay this method until after 7612 * the collection resets itself 7613 */ 7614 if( Cargo.Helper.NextProjectNeedsToPaginate() && !dont_check ) { 7615 7616 /* Delay this event, but only fire it once, so remove it too 7617 * Note: Stays above the Pagination because memory fires too quick */ 7618 Cargo.Event.on("pagination_complete", function() { 7619 Cargo.View.Main.NextProject(); 7620 this.off("pagination_complete"); 7621 }); 7622 7623 if( Cargo.Helper.GetTotalPages() == Cargo.API.Config.current_page ) { 7624 if( Cargo.Helper.IsAutopaginate() ) { 7625 /* If we reached the end and it is autopaginated, go back to the start */ 7626 this.NextProject(false, true); 7627 } else { 7628 /* If we are on the last page, go to first */ 7629 Cargo.View.Main.GoToPage(1); 7630 } 7631 7632 } else { 7633 /* Get the next page */ 7634 Cargo.View.Main.NextPage(); 7635 } 7636 7637 7638 } else { 7639 /* Get the next purl */ 7640 var purl = Cargo.Helper.GetNextProjectUrl(); 7641 var href = "/" + purl; 7642 7643 /* Navigate to the new route */ 7644 if( Cargo.View.hasOwnProperty('ProjectDetail') ) { 7645 Cargo.Event.trigger("add_history", href); 7646 } else { 7647 document.location.href = Cargo.Helper.GetBaseUrl(true, true) + href; 7648 } 7649 7650 /* Cargo events */ 7651 Cargo.Event.trigger("next_project", Cargo.Helper.GetPidFromPurl(purl)); 7652 } 7653 7654 }, 7655 7656 /** 7657 * Previous project 7658 * If we are on the first project of a page, go to the previous page 7659 * If we are on the first project of the first page, go to last page 7660 * Works with any tag 7661 * Usage: 7662 * <a href="#" rel="prev_project">Next project</a> 7663 * Or 7664 * Cargo.View.Main.PrevProject(); 7665 */ 7666 PrevProject : function( e, dont_check ) { 7667 if( e ) { 7668 /* Remove default action (if there is one) */ 7669 e.preventDefault(); 7670 } 7671 7672 /* 7673 * If the next project needs to paginated 7674 * then paginate and delay this method until after 7675 * the collection resets itself 7676 */ 7677 if( Cargo.Helper.PrevProjectNeedsToPaginate() && !dont_check && !Cargo.Helper.IsAutopaginate() ) { 7678 /* Delay this event, but only fire it once, so remove it too 7679 * Note: Stays above the Pagination because memory fires too quick */ 7680 Cargo.Event.on("pagination_complete", function() { 7681 /* Set value to true to not perform this check next time */ 7682 Cargo.View.Main.PrevProject( e, true ); 7683 this.off("pagination_complete"); 7684 }); 7685 7686 if( Cargo.API.Config.current_page == 1 ) { 7687 /* If we are on the first page, go to last */ 7688 Cargo.View.Main.GoToPage( Cargo.Helper.GetTotalPages() ); 7689 7690 } else { 7691 /* Get the prev page */ 7692 Cargo.View.Main.PrevPage(); 7693 } 7694 7695 7696 } else { 7697 /* Get the prev purl */ 7698 var purl = Cargo.Helper.GetPrevProjectUrl(); 7699 var href = "/" + purl; 7700 7701 /* Navigate to the new route */ 7702 if( Cargo.View.hasOwnProperty('ProjectDetail') ) { 7703 Cargo.Event.trigger("add_history", href); 7704 } else { 7705 document.location.href = Cargo.Helper.GetBaseUrl(true, true) + href; 7706 } 7707 7708 7709 /* Cargo events */ 7710 Cargo.Event.trigger("prev_project", Cargo.Helper.GetPidFromPurl(purl)); 7711 } 7712 }, 7713 7714 /** 7715 * Random project 7716 * Works with any tag 7717 * Usage: 7718 * <a href="#" rel="random_project">Random project</a> 7719 * Or 7720 * Cargo.View.Main.RandomProject(); 7721 */ 7722 RandomProject : function( e ) { 7723 if( e ) { 7724 /* Remove default action (if there is one) */ 7725 e.preventDefault(); 7726 } 7727 7728 if( Cargo.View.hasOwnProperty('ProjectDetail') ) { 7729 Cargo.API.GetRandomProject(); 7730 7731 } else { 7732 // Get a random number 7733 var random = Math.floor(Math.random() * Cargo.Helper.GetTotalProjectsInMemory()) + 1; 7734 var obj = Cargo.Helper.GetProjectModelAndPageByKey(random); 7735 var cur_pid = Cargo.Helper.GetPidFromPurl( Cargo.Model.Project.GetProjectUrl() ); 7736 7737 // If there is a project open, make sure this is not that project 7738 if( obj.model && cur_pid == obj.model.get('id') ) { 7739 // Randomize agail 7740 random = Math.floor(Math.random()*Cargo.Helper.GetTotalProjectsInMemory()); 7741 obj = Cargo.Helper.GetProjectModelAndPageByKey(random); 7742 } 7743 7744 if( obj.model && obj.model.get('id') && cur_pid != obj.model.get('id') ) { 7745 document.location.href = obj.model.get('url'); 7746 7747 // Cargo events 7748 Cargo.Event.trigger("random_project", obj.model.get('id') ); 7749 7750 } else { 7751 this.RandomProject(); 7752 } 7753 } 7754 }, 7755 7756 7757 /** 7758 * Next page 7759 * Works with any tag 7760 * Usage: 7761 * <a href="#" rel="next_page">Next project</a> 7762 * Or 7763 * Cargo.View.Main.NextPage(); 7764 */ 7765 NextPage : function( e ) { 7766 if( e ) { 7767 /* Remove default action (if there is one) */ 7768 e.preventDefault(); 7769 } 7770 7771 var self = this; 7772 7773 /* Set the next page */ 7774 var current_page = Cargo.API.Config.current_page; 7775 var next_page = current_page+1; 7776 7777 /* Call the API for the next page */ 7778 this.GoToPage(next_page); 7779 7780 /* Cargo events */ 7781 Cargo.Event.trigger("next_page", next_page ); 7782 7783 }, 7784 7785 /** 7786 * Previous page 7787 * Works with any tag 7788 * Usage: 7789 * <a href="#" rel="prev_page">Next project</a> 7790 * Or 7791 * Cargo.View.Main.PrevPage(); 7792 */ 7793 PrevPage : function( e ) { 7794 if( e ) { 7795 /* Remove default action (if there is one) */ 7796 e.preventDefault(); 7797 } 7798 7799 /* Set the prev page */ 7800 var current_page = Cargo.API.Config.current_page; 7801 var prev_page = current_page-1; 7802 7803 /* Call the API for the next page */ 7804 this.GoToPage(prev_page); 7805 7806 /* Cargo events */ 7807 Cargo.Event.trigger("prev_page", prev_page ); 7808 }, 7809 7810 /** 7811 * Get a specific page 7812 */ 7813 GoToPage : function( page ) { 7814 /* Show the loading animation */ 7815 Cargo.Helper.ShowLoadingAnimation( ); 7816 7817 /* Set the prev page */ 7818 var current_page = Cargo.API.Config.current_page; 7819 7820 /* Call the API for the next page */ 7821 Cargo.API.SetProjectsPage( page, current_page ); 7822 Cargo.API.SetNavigationPage( page, current_page ); 7823 }, 7824 7825 /** 7826 * This method grabs the custom HTML out of the 7827 * edit frame 7828 */ 7829 WriteCustomHtmlPreview : function() { 7830 7831 if(parent.editframe !== undefined){ 7832 var customhtml = parent.editframe.document.getElementById("syntax_highlight").value; 7833 customhtml = Cargo.Core.Handlebars.RenderOnlyAssets(customhtml); 7834 $("customhtml").html(customhtml); 7835 } else if(typeof parent.Cargo !== "undefined" && parent.Cargo.hasOwnProperty('Editor')) { 7836 7837 if(parent.Cargo.Editor.previewCustomHTML !== undefined) { 7838 7839 var customhtml = parent.Cargo.Editor.previewCustomHTML; 7840 customhtml = Cargo.Core.Handlebars.RenderOnlyAssets(customhtml); 7841 $("customhtml").html(customhtml); 7842 7843 } 7844 7845 } 7846 7847 }, 7848 7849 /** 7850 * This method grabs the CSS out of the 7851 * edit frame 7852 */ 7853 WriteCustomCSSPreview : function() { 7854 var stylesheet = parent.editframe.document.getElementById("syntax_highlight").value; 7855 stylesheet = Cargo.Core.Handlebars.RenderOnlyAssets(stylesheet); 7856 $("body").append('<style>'+stylesheet+'</style>'); 7857 $("head link[rel='stylesheet']").remove(); 7858 }, 7859 7860 /* 7861 * For ajax history, set the title of the page 7862 */ 7863 SetWindowTitle : function(is_unload) { 7864 if(!is_unload) { 7865 if(Cargo.Model.Project.GetTitleNoHtml()) { 7866 document.title = Cargo.Model.Project.GetTitleNoHtml() + " - " + Cargo.Model.Site.GetWebsiteTitle(); 7867 } 7868 7869 } else { 7870 document.title = Cargo.Model.Site.GetWebsiteTitle(); 7871 } 7872 }, 7873 7874 /* 7875 * For ajax history, set the title of the page 7876 */ 7877 SetDataType : function(type) { 7878 this.$el.attr("data-pagetype", type); 7879 if(type) this.$el.addClass("open"); 7880 else this.$el.removeClass("open"); 7881 }, 7882 7883 /** 7884 * Set a class for the start project, if this is one 7885 * Is called from the router when we find out it is the home route 7886 * and this is a start project 7887 */ 7888 SetStartProjectClass : function() { 7889 if( Cargo.API.Config.start_project ) { 7890 this.$el.addClass('start_project'); 7891 } 7892 }, 7893 7894 /** 7895 * This method is called every project open 7896 */ 7897 RemoveStartProjectClass : function() { 7898 this.$el.removeClass('start_project'); 7899 } 7900 7901 }); 7902 7903 7904 7905 7906/** 7907 * This Feed view is here to kick ass and chew bubblegum. Let's hope it's not out of bubble gum. 7908 * 7909 */ 7910 7911 Cargo.o.view.Feed = Backbone.View.extend({ 7912 7913 initialize: function(options) { 7914 7915 var self = this; 7916 7917 this.options = options; 7918 this.initialized = false;
7919 7920 if( typeof this.collection == "object" ) { 7921 7922 this.listenTo(this.collection, "add", this.render); 7923 this.listenTo(this.collection, "change", this.render); 7924 this.listenTo(this.collection, 'reset', this.reset); 7925 7926 } 7927 7928 Cargo.Event.on('first_project_collection_reset', function(){ 7929 self.initialized = true; 7930 }); 7931 7932 }, 7933 7934 render : function(model) { 7935 7936 /* Get the template for this view (Prevents first load render) */ 7937 var template = Cargo.Template.Get('Feed'), 7938 pid = (model.get('id') !== undefined ? model.get('id') : false), 7939 projectContainer = $("[data-id='" + pid + "']", this.$el); 7940 7941 /* 7942 * If a template exists, render it 7943 */ 7944 7945 if( template && this.initialized === true) { 7946 7947 /* Render the template and get the markup */ 7948 var data_model = Cargo.API.GetDataPackage( 'projects', model.toJSON() ), 7949 markup = Cargo.Core.Handlebars.Render( template, data_model); 7950 7951 if( projectContainer.length > 0 ){ 7952 7953 // update project 7954 projectContainer[0].outerHTML = markup; 7955 7956 } else { 7957 7958 // create new project 7959 this.$el.append(markup); 7960 7961 } 7962 7963 // setting outerHTML loses the reference & 7964 // creating a new project requires looking up the new el too. 7965 projectContainer = $("[data-id='" + pid + "']", this.$el); 7966 7967 } 7968 7969 this.markNodesAsOriginal(projectContainer, pid); 7970 7971 }, 7972 7973 reset : function() { 7974 7975 /* Make a reference to self */ 7976 var self = this; 7977 7978 /* Get the template for this view (Prevents first load render) */ 7979 var template = Cargo.Template.Get('Feed'); 7980 7981 /* 7982 * If a template exists, render it 7983 */ 7984 if( template ) { 7985 7986 /* Create a package of data for the render */ 7987 var data_model = Cargo.API.GetDataPackage( 'projects', this.collection.toJSON() ); 7988 7989 /* Render the template and get the markup */ 7990 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 7991 7992 /* Empty the container */ 7993 this.$el.empty(); 7994 this.$el.append( markup ); 7995 7996 var i, 7997 pid; 7998 7999 for(i = 0; i < data_model.projects.length; i++){ 8000 8001 pid = data_model.projects[i].id; 8002 this.markNodesAsOriginal($("[data-id='" + pid + "']", this.$el), pid); 8003 8004 } 8005 } // end template 8006 8007 }, 8008 8009 markNodesAsOriginal: function(projectContainer, pid){ 8010 8011 var projectContent = projectContainer.find('projectcontent')[0]; 8012 8013 if(projectContent !== undefined){ 8014 8015 if(Node.prototype.hasOwnProperty('setSaveable')){ 8016 this.getAllDescendants(projectContent, function(node){ 8017 node.setSaveable(true); 8018 }); 8019 } 8020 8021 var parent = projectContent.parentNode; 8022 8023 // fyi: ce = cargo editor 8024 parent.setAttribute('data-ce-host', 'true'); 8025 parent.setAttribute('data-ce-model-id', pid); 8026 8027 while (projectContent.childNodes.length > 0) { 8028 parent.insertBefore(projectContent.childNodes[0], projectContent); 8029 } 8030 8031 parent.removeChild(projectContent); 8032 8033 } 8034 8035 }, 8036 8037 getAllDescendants: function(node, callback) { 8038 8039 // recursively finds all childnodes and fires a callback for every node found 8040 for (var i = 0; i < node.childNodes.length; i++) { 8041 8042 var child = node.childNodes[i], 8043 result = callback(child); 8044 8045 if(result === false){ 8046 return false; 8047 } 8048 8049 // if "continue" is being returned from the callback we don't go down 8050 // the subtree of the current child. 8051 if(result !== "continue"){ 8052 // recursively go down the tree. 8053 this.getAllDescendants(child, callback); 8054 } 8055 8056 } 8057 8058 } 8059 8060 }); 8061 8062 Cargo.o.view.ProjectDetail = Backbone.View.extend({ 8063 el: '#maincontainer', 8064 8065 initialize: function() { 8066 this.Data = { 8067 "total_tries" : 0, 8068 "max_tries" : 5, 8069 "first_load" : true 8070 }; 8071 8072 // Set an event to wait for the model to change 8073 //this.collection.bind("empty", this.CloseProject, this); 8074 8075 // Set an event to wait for the model to change 8076 this.collection.bind("change", this.render, this); 8077 8078 var model = this.collection, 8079 pid = (model && model.GetId()) ? model.GetId() : false; 8080 8081 this.markNodesAsOriginal(pid); 8082 8083 /* 8084 * If we are editing a project in the admin 8085 */ 8086 // if( Cargo.API.Config.adminedit ) { 8087 // this.AdminEditProject(); 8088 // } 8089 8090 }, 8091 8092 render : function(e) { 8093 8094 if(this.preventRender === true) { 8095 console.log('Prevented project render'); 8096 return; 8097 } 8098 8099 // Set the model for shorthand 8100 var model = this.collection, 8101 _self = this; 8102 8103 /* If there is no model, and no model.id, close the project */ 8104 if( model && !model.GetId() ) { 8105 this.CloseProject(); 8106 8107 /* Show the loading animations */ 8108 Cargo.Helper.HideLoadingAnimation(); 8109 8110 return; 8111 } 8112 8113 /* Make sure we have a model */ 8114 else if( model ) { 8115 var self = this; 8116 var pid = (model && model.GetId()) ? model.GetId() : false;
8117 var template = Cargo.Template.Get( "ProjectDetail" ); 8118 8119 this.Data.pid = pid; 8120 8121 /* 8122 * Probably this is being called before the template has been loaded 8123 * set a timeout and recall this method when the template is loaded 8124 * However, there might be something really wrong here, so keep track 8125 * of how many times we try. If it is too many, reload the whole page 8126 * at the direct link 8127 */ 8128 if( !template ) { 8129 if( Cargo.View.ProjectDetail.Data.total_tries >= Cargo.View.ProjectDetail.Data.max_tries ) { 8130 document.location.href = model.GetDirectLink(); 8131 8132 } else { 8133 setTimeout(function() { 8134 Cargo.View.ProjectDetail.Data.total_tries++; 8135 Cargo.View.ProjectDetail.render(); 8136 },100); 8137 8138 } 8139 return false; 8140 } 8141 8142 /* Create a json object for the model wrapped in the project label */ 8143 var data_model = Cargo.API.GetDataPackage( "project", model.toJSON() ); 8144 8145 /* Render the template and get the markup */ 8146 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 8147 8148 /* Wait for the deferred project transition event to complete */ 8149 $.when( Cargo.Event.ShowProjectTransition(pid, markup, model) ).then(function() { 8150 /* Write the content */ 8151 self.$el.html( markup ).show(); 8152 8153 _self.markNodesAsOriginal(pid); 8154 8155 /* Fire the Cargo event on complete */ 8156 Cargo.Event.trigger("project_load_complete", pid); 8157 8158 /* Increment the view count */ 8159 self.IncrementView(); 8160 8161 /* Reset the total tries count */ 8162 Cargo.View.ProjectDetail.Data.total_tries = 0; 8163 8164 self.Data.first_load = false; 8165 }); 8166 8167 } 8168 8169 }, 8170 8171 /** 8172 * Remove the main view 8173 */ 8174 CloseProject : function() { 8175 // Set the model for shorthand 8176 var self = this; 8177 var model = this.collection; 8178 8179 // On first load, do not use transitions 8180 if(this.Data.first_load === true) { 8181 this.DoCloseActions(this.Data.pid); 8182 } else { 8183 /* Wait for the deferred project transition event to complete */ 8184 $.when( Cargo.Event.ShowIndexTransition(this.Data.pid, model) ).then(function() { 8185 self.DoCloseActions(self.Data.pid); 8186 }); 8187 } 8188 8189 }, 8190 8191 /** 8192 * Do the actions to actually close the project 8193 * This is offloaded so there is a way of bypassing the transition deferment 8194 * @param {Number} pid Project ID 8195 */ 8196 DoCloseActions: function(pid) { 8197 /* Before close events */
8198 Cargo.Event.trigger("show_index_start", pid ); 8199 8200 this.$el.empty().hide(0); 8201 8202 /* After close events */ 8203 Cargo.Event.trigger("show_index_complete", pid ); 8204 8205 this.Data.first_load = false; 8206 }, 8207 8208 8209 /** 8210 * This is what happens on a permalink 8211 */ 8212 Permalink : function(purl) { 8213 /* 8214 * If this is an old browser #link, do a different permalink 8215 */ 8216 if(Cargo.Helper.isOldIE()) { 8217 this.IePermalink(purl); 8218 } else { 8219 this.el.style.display = "block"; 8220 8221 Cargo.Event.trigger("direct_link_loaded", Cargo.Model.Project.GetId(), location.pathname); 8222 Cargo.Event.trigger("project_load_complete", Cargo.Model.Project.GetId()); 8223 } 8224 8225 // Cargo.API.Config.is_direct_link = false; 8226 8227 this.Data.first_load = false; 8228 }, 8229 8230 /** 8231 * When an old browser loads a permalink, 8232 * it comes in the form or #Project and so the data 8233 * is not yet loaded because PHP does not know about it 8234 * So we need to load the data 8235 */ 8236 IePermalink : function(purl) { 8237 /* 8238 * Not sure why, but this has to sit in a timeout 8239 */ 8240 setTimeout(function() { 8241 /* 8242 * Get and set the new single project 8243 * This will trigger an event that will automatically 8244 * update the view 8245 */ 8246 Cargo.API.GetSingleProject( purl ); 8247 8248 }, 500); 8249 }, 8250 8251 /** 8252 * Change the background color based on project model 8253 * This is called from the projectloadcomplete event 8254 * Inject the project detail element with a style tag 8255 * This is done because when the element is removed on close 8256 * the style tag will go with it. So we don't need to remember 8257 * states 8258 */ 8259 ChangeBgColorFromModel : function() { 8260 var model = this.collection; 8261 8262 if( model && !model.GetId() ) { 8263 return; 8264 8265 } else if( model.GetBgColor() ) { 8266 var tag = '<style id="site_bg_color"> body { background : ' + model.GetBgColor() + ' } </style>'; 8267 this.$el.append(tag); 8268 } 8269 }, 8270 8271 /** 8272 * This method removes all content but the project detail 8273 * It is for when editing a project in the admin 8274 */ 8275 AdminEditProject : function() { 8276 // Find any object with the adminedit flag and include that 8277 // var options = $("[data-options]").clone(); 8278 // $("body").css("margin", "35px"); 8279 // $("body").html(this.$el).append(options); 8280 8281 // Cargo.Event.trigger("admin_edit_load_complete"); 8282 }, 8283 8284 /** 8285 * This method is triggered by the admin editor 8286 * It expects project content to be passed to it 8287 */ 8288 SaveContent : function(content) { 8289 // Set the local content 8290 var model = this.collection; 8291 model.set('content', content, { silent : true }); 8292 8293 var collection = null; 8294 8295 // Get the right collection 8296 if(model.get('type') == "page") { 8297 collection = Cargo.Collection.Pages; 8298 } else { 8299 collection = Cargo.Collection.Projects; 8300 } 8301 8302 // Update the right model in the collection 8303 var collection_model = collection.where({ id : model.get('id') })[0]; 8304 8305 if(collection_model) { 8306 collection_model.set('content', content, { silent : true }); 8307 } 8308 8309 }, 8310 8311 /** 8312 * Increment the view count on project open 8313 */ 8314 IncrementView : function() { 8315 var self = this; 8316 8317 /* Put a timeout on this so that the browser can do render 8318 * before it has to do this ajax 8319 */ 8320 8321 setTimeout(function() { 8322 if( self.collection.GetId() != "none" ) { 8323 $.post('/dispatch/project/addview', { pid: self.collection.GetId() }); 8324 } 8325 }, 500); 8326 }, 8327 8328 markNodesAsOriginal: function(pid){ 8329 8330 var projectContent = this.$el.find('projectcontent')[0]; 8331 8332 if(projectContent !== undefined){ 8333 8334 if(Node.prototype.hasOwnProperty('setSaveable')){ 8335 this.getAllDescendants(projectContent, function(node){ 8336 node.setSaveable(true); 8337 }); 8338 } 8339 8340 var parent = projectContent.parentNode; 8341 8342 // fyi: ce = cargo editor 8343 parent.setAttribute('data-ce-host', 'true'); 8344 parent.setAttribute('data-ce-model-id', pid); 8345 8346 while (projectContent.childNodes.length > 0) { 8347 parent.insertBefore(projectContent.childNodes[0], projectContent); 8348 } 8349 8350 parent.removeChild(projectContent); 8351 } 8352 8353 }, 8354 8355 getAllDescendants: function(node, callback) { 8356 8357 // recursively finds all childnodes and fires a callback for every node found 8358 for (var i = 0; i < node.childNodes.length; i++) { 8359 8360 var child = node.childNodes[i], 8361 result = callback(child); 8362 8363 if(result === false){ 8364 return false;
8365 } 8366 8367 // if "continue" is being returned from the callback we don't go down 8368 // the subtree of the current child. 8369 if(result !== "continue"){ 8370 // recursively go down the tree. 8371 this.getAllDescendants(child, callback); 8372 } 8373 8374 } 8375 8376 } 8377 }); 8378 8379 8380 8381 8382 Cargo.o.view.Pagination = Backbone.View.extend({ 8383 initialize: function() { 8384 8385 /* Defer the toggle of the project until after the collection is reset */ 8386 Cargo.Event.on("project_collection_reset", function( ) { 8387 Cargo.View.Pagination.ChangePage( Cargo.API.Config.current_page ); 8388 }); 8389 8390 }, 8391 8392 /* 8393 * Receives a model input (probably from the ChangePage method) 8394 * Renders the template 8395 */ 8396 render : function( model ) { 8397 8398 /* Get the template for this view (Prevents first load render) */ 8399 var template = Cargo.Template.Get("Pagination"); 8400 8401 /* Only render if we found a template */ 8402 if( template ) { 8403 8404 /* Render the template and get the markup */ 8405 var markup = Cargo.Core.Handlebars.Render( template, model ); 8406 8407 this.$el.html( markup ); //.show(); 8408 8409 } 8410 8411 /* Cargo event */ 8412 Cargo.Event.trigger("pagination_complete", Cargo.API.Config.current_page ); 8413 }, 8414 8415 /** 8416 * Handle a page change 8417 * 8418 */ 8419 ChangePage : function( next_page ) { 8420 var total = Cargo.Helper.GetTotalPages(); 8421 8422 Cargo.Helper.ShowLoadingAnimation( ); 8423 8424 /* 8425 * Determine what type of pagination message to show 8426 */ 8427 // If there is only one page, don't show anything 8428 if( next_page == 1 && total == 1) { 8429 pagination = ""; 8430 } 8431 // If we are on the first page and more then one page 8432 else if(next_page == 1 && total > 1) { 8433 pagination = { 8434 "next_page" : { 8435 "current" : next_page, 8436 "total" : total 8437 } 8438 }; 8439 } 8440 // If we not on the last page 8441 else if( next_page == total ) { 8442 pagination = { 8443 "prev_page" : { 8444 "current" : next_page, 8445 "total" : total 8446 } 8447 }; 8448 } 8449 // If we are past the first page, before the last page 8450 else if( next_page > 1 && next_page < total ) { 8451 pagination = { 8452 "next_prev_page" : { 8453 "current" : next_page, 8454 "total" : total 8455 } 8456 }; 8457 } 8458 8459 8460 this.render(pagination); 8461 } 8462 8463 8464 }); 8465 8466 8467 8468 Cargo.o.view.Thumbnail = Backbone.View.extend({ 8469 8470 initialize: function() { 8471 this.Data = { 8472 /* How many models do we have */ 8473 model_count : 0 8474 }; 8475 8476 /* Set an event to wait for the model to change */ 8477 this.listenTo(this.collection, "reset", this.render); 8478 this.listenTo(this.collection, "add", this.add); 8479 }, 8480 8481 /** 8482 * This method will re-render the entire contents of the container 8483 * this is meant for replacing all content in a standard pagination 8484 */ 8485 render : function( ) { 8486 8487 /* Make a reference to self */ 8488 var self = this; 8489 8490 /* Get the template for this view (Prevents first load render) */ 8491 var template = Cargo.Template.Get("Thumbnail"); 8492 8493 /* 8494 * If a template exists, render it 8495 */ 8496 if( template ) { 8497 /* Create a package of data for the render */ 8498 var data_model = Cargo.API.GetDataPackage( "projects", this.collection.toJSON() ); 8499 8500 /* Render the template and get the markup */ 8501 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 8502 8503 /* Empty the container */ 8504 this.$el.empty(); 8505 this.$el.append( markup ); 8506 8507 Cargo.Event.trigger("project_collection_reset"); 8508 } 8509 8510 }, 8511 8512 /** 8513 * The add event is for adding a single item into the collection 8514 * this is intended for autopagination 8515 */ 8516 add : function( model ) { 8517 var self = this; 8518 8519 /* Count how many models we have for autopagination */ 8520 this.Data.model_count++; 8521 8522 /* Get the template for this view */ 8523 var template = Cargo.Template.Get("Thumbnail"); 8524 8525 var in_thumb = ($("[href='"+model.get('url')+"']", self.$el).length > 0); 8526 var in_data = ($("[data-id='"+model.get('id')+"']", self.$el).length > 0); 8527 8528 /* Verify that this model is not already onscreen */ 8529 if( template && !in_thumb && !in_data ) { 8530 /* Create a package of data for the render */ 8531 var data_model = Cargo.API.GetDataPackage( "projects", model.toJSON() ); 8532 8533 /* Render the template and get the markup */ 8534 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 8535 8536 /* Check if this is in a set and needs to be added to one */ 8537 if( 8538 Cargo.Model.DisplayOptions.GetUseSets() && 8539 model.get("set_id") != 0 && 8540 $("[data-setid='"+model.get("set_id")+"']", self.$el).length > 0 8541 ) { 8542 $("[data-setid='"+model.get("set_id")+"']", self.$el).append( markup ); 8543 } 8544 /* Prepend this to the autopagination handle */ 8545 else { 8546 this.$el.append( markup ); 8547 } 8548 } 8549 8550 /* Only fire this if the Pagination view is not loaded to prevent double calls 8551 * and when the amount of projects on screen is the same as in the collection 8552 */ 8553 if( !Cargo.View.Pagination && this.collection.length == this.Data.model_count) { 8554 Cargo.Event.trigger("project_collection_reset"); 8555 } 8556 }, 8557 8558 /** 8559 * Add an active state to the navigation 8560 * remove old active states first 8561 */ 8562 AddActive : function(pid) { 8563 var self = this; 8564 if( !pid ) { 8565 pid = Cargo.Model.Project.GetId(); 8566 } 8567 this.RemoveActive(); 8568 $("[data-id='"+pid+"']", self.$el).addClass("active"); 8569 }, 8570 8571 /** 8572 * Remove active state to the navigation 8573 */ 8574 RemoveActive : function() { 8575 var self = this; 8576 $(".active", self.$el).removeClass("active"); 8577 }, 8578 8579 /** 8580 * Checks the display options for hidden thumbs below projects and/or pages 8581 * and if it is set, then hide them 8582 */ 8583 CheckAndHideThumbs : function() { 8584 var type = Cargo.Helper.GetCurrentPageType(); 8585 if( type == "project" && !Cargo.Model.DisplayOptions.GetThumbsBelowProjects() ) { 8586 this.$el.hide(); 8587 8588 } else if( type == "page" && !Cargo.Model.DisplayOptions.GetThumbsBelowPages() ) { 8589 this.$el.hide(); 8590 8591 } else { 8592 this.ShowThumbs(); 8593 } 8594 }, 8595 8596 /** 8597 * Show the thumbnails again 8598 */ 8599 ShowThumbs : function() { 8600 this.$el.show(); 8601 } 8602 }); 8603 8604 Cargo.o.view.Navigation = Backbone.View.extend({ 8605 8606 Data : { 8607 first_load_complete : false 8608 }, 8609 8610 initialize: function() { 8611 /* Set an event to wait for the model to change */ 8612 this.listenTo(this.collection, "reset", this.render); 8613 this.listenTo(this.collection, "change", this.render); 8614 this.listenTo(this.collection, "add", this.add); 8615 8616 Cargo.Event.on('first_navigation_collection_reset', function() { 8617 Cargo.View.Navigation.Data.first_load_complete = true; 8618 }); 8619 }, 8620 8621 events: { 8622 "click .set_link > a[href='#']" : "setClick" 8623 }, 8624 8625 /** 8626 * This method will re-render the entire contents of the container 8627 * this is meant for replacing all content in a standard pagination 8628 */ 8629 render : function( ) { 8630 8631 /* Make a reference to self */ 8632 var self = this; 8633 8634 /* Get the template for this view (Prevents first load render) */ 8635 var template = Cargo.Template.Get("Navigation"); 8636 8637 /* 8638 * If a template exists, render it 8639 */ 8640 if( template ) { 8641 /* Create a package of data for the render */ 8642 var data_model = Cargo.API.GetDataPackage( "navigation", this.collection.toJSON() ); 8643 8644 /* Render the template and get the markup */ 8645 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 8646 8647 /* Empty the container */ 8648 this.$el.empty(); 8649 this.$el.append( markup ); 8650 8651 } 8652 8653 Cargo.Event.trigger("navigation_reset"); 8654 8655 }, 8656 8657 /** 8658 * The add event is for adding a single item into the collection 8659 * this is intended for autopagination 8660 */ 8661 add : function( model ) { 8662 var self = this; 8663 8664 /* Get the template for this view (Prevents first load render) */ 8665 var template = Cargo.Template.Get("Navigation"); 8666 8667 /* Verify that this model is not already onscreen */ 8668 if( 8669 this.Data.first_load_complete && 8670 $("[href='"+model.get('url')+"']", self.$el).length == 0 && 8671 $("[id$='"+model.get('id')+"']", self.$el).length == 0 8672 ) { 8673 8674 /* Create a package of data for the render */ 8675 var data_model = Cargo.API.GetDataPackage( "navigation", model.toJSON() ); 8676 8677 /* Render the template and get the markup */ 8678 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 8679 8680 /* Check if this is in a set and needs to be added to one */ 8681 if( 8682 Cargo.Model.DisplayOptions.GetUseSets() && 8683 model.get("set_id") != 0 && 8684 $("[data-setid='"+model.get("set_id")+"']", self.$el).length > 0 8685 ) { 8686 $("[data-setid='"+model.get("set_id")+"']", self.$el).append( markup ); 8687 } 8688 /* Prepend this to the autopagination handle */ 8689 else { 8690 this.$el.append( markup ); 8691 } 8692 } 8693 }, 8694 8695 setClick : function(e) { 8696 8697 if( e ) { 8698 /* Remove default action */ 8699 e.preventDefault(); 8700
8701 if(Cargo.Model.DisplayOptions.GetSetsCanToggle()){ 8702 var set = $(e.currentTarget).closest('li.set_link'); 8703 var nav = this.el; 8704 8705 if(set.hasClass('open')){ 8706 set.removeClass('open').addClass('closed'); 8707 } else if(set.hasClass('closed')){ 8708 if(Cargo.Model.DisplayOptions.GetSetsOneAtATime()){ 8709 $(nav).find('li.set_link').removeClass('open').addClass('closed'); 8710 } 8711 set.removeClass('closed').addClass('open'); 8712 } else { 8713 // assume it's open 8714 set.addClass('closed'); 8715 } 8716 8717 Cargo.Event.trigger("navigation_set_toggle"); 8718 } 8719 } 8720 }, 8721 8722 8723 /** 8724 * Add an active state to the navigation 8725 * remove old active states first 8726 */ 8727 AddActive : function(pid) { 8728 var self = this; 8729 if( !pid ) { 8730 pid = Cargo.Model.Project.GetId(); 8731 } 8732 this.RemoveActive(); 8733 $("[data-id='"+pid+"']", self.$el).addClass("active"); 8734 }, 8735 8736 /** 8737 * Remove active state to the navigation 8738 */ 8739 RemoveActive : function() { 8740 var self = this; 8741 $(".active", self.$el).removeClass("active"); 8742 }, 8743 8744 /** 8745 * Add an active state to the set 8746 */ 8747 AddSetActive : function() { 8748 var self = this; 8749 var set_id = Cargo.API.Config.set_id ? Cargo.API.Config.set_id : Cargo.Model.Project.GetSetId(); 8750 if( set_id ) { 8751 $("[data-id='"+set_id+"']", self.$el).addClass("active"); 8752 } 8753 }, 8754 8755 /** 8756 * Add an active state to the set 8757 */ 8758 RemoveSetActive : function() { 8759 var self = this; 8760 var set_id = Cargo.API.Config.set_id ? Cargo.API.Config.set_id : Cargo.Model.Project.GetSetId(); 8761 if( set_id ) { 8762 $("[data-id='"+set_id+"']", self.$el).removeClass("active"); 8763 } 8764 } 8765 }); 8766 8767 8768 Cargo.o.view.Freshbox = Backbone.View.extend({ 8769 8770 events: { 8771 "click #fullscreen" : "InitFullscreen", 8772 "click #fullscreen_prev" : "Prev", 8773 "click #fullscreen_next" : "Next", 8774 "click #fullscreen_close" : "Close", 8775 "click #fullscreen_zoom" : function(e){ 8776 this.toggleZoom(); 8777 this.Data.zoomEnabled =! this.Data.zoomEnabled; 8778 return false; 8779 }, 8780 "click #freshbox" : "Close", 8781 "click img" : "LightboxClick", 8782 "mouseover img" : "LightboxHover" 8783 }, 8784 8785 prevent_smart_image_picking: false, 8786 8787 initialize: function() { 8788 8789 Cargo.Event.on('lightbox-init', function(){ 8790 8791 this.InitKeyboardNav(); 8792 8793 if(this.isFullscreen) { 8794 Cargo.Event.trigger("fullscreen_open"); 8795 } else { 8796 Cargo.Event.trigger("lightbox_open"); 8797 } 8798 8799 }, this); 8800 8801 Cargo.Event.on('lightbox-destroy', function(){ 8802 8803 this.DestroyKeyboardNav(); 8804 8805 Cargo.Event.trigger("fullscreen_destroy_hotkeys"); 8806 8807 if(this.isFullscreen) { 8808 Cargo.Event.trigger("fullscreen_close"); 8809 } else { 8810 Cargo.Event.trigger("lightbox_close"); 8811 } 8812 8813 }, this); 8814 8815 Cargo.Event.trigger('fullscreen-initialized', this); 8816 8817 }, 8818 8819 /** 8820 * Write out the freshbox element to the page 8821 * Uses the template that was already loaded 8822 */ 8823 render : function() { 8824 8825 }, 8826 8827 /** 8828 * Fullscreen 8829 * Called from ProjectDetail 8830 * Opened by clicking the fulllscreen button 8831 * Opens images fullscreen via HTML5 8832 */ 8833 InitFullscreen : function( e ) { 8834 8835 if( e ) { 8836 e.preventDefault(); 8837 this.clicked_img = $(e.currentTarget); 8838 } 8839 8840 this.isFullscreen = true; 8841 8842 this.InitSlideshow(); 8843 8844 /* Remove default action */ 8845 /*if( e ) e.preventDefault(); 8846 8847 this.clicked_img = $(e.currentTarget); 8848 8849 this.InitSlideshow(); 8850 8851 // Hide until open if not IE or iOS or an old browser 8852 if( ! Cargo.Helper.isIE() && (! Cargo.Helper.isIOS() || ! Cargo.Helper.AllowsFullScreen()) ) { 8853 $("#freshbox").css("visibility", "hidden"); 8854 } 8855 8856 this.OpenFullscreen(); 8857 8858 this.Next();*/ 8859 8860 }, 8861 8862 /** 8863 * Lightbox view 8864 * Opened by clicking on an img tag inside the content 8865 */ 8866 LightboxClick : function( e ) { 8867 8868 this.clicked_img = $(e.currentTarget); 8869 8870 /* Don't allow this behavior on slideshow thumbs */ 8871 if( $(this.clicked_img).closest('.slideshow_thumbnails').length > 0 ) { 8872 return false; 8873 } 8874 8875 /* Always verify that we want the lightbox */ 8876 if( Cargo.Model.DisplayOptions.GetLightboxView() ) { 8877 /* Verify there is a larger version available (and is a Cargo image) */ 8878 if( typeof $(this.clicked_img).attr("src_o") != "undefined" ) { 8879 8880 if( $(this.clicked_img).parent("a").length == 0 ) { 8881 /* Remove default action */ 8882 if( e ) e.preventDefault(); 8883 8884 this.isFullscreen = false;
8885 8886 this.InitSlideshow(e.currentTarget); 8887 8888 var self = this; 8889 8890 /* Set the first image to be the clicked image 8891 * by looping through all images and finding a match 8892 * Any image that is not matched, gets moved to the back of the stack */ 8893 $("#fullscreen_imgs img").each(function() { 8894 if( $(this).attr("src_o") == $(e.currentTarget).attr("src_o") ) { 8895 /* Show the first image by going "next" */ 8896 self.Next(); 8897 return false; 8898 8899 } else { 8900 /* This is not it, so progress to the next frame */ 8901 self.Next(); 8902 } 8903 8904 }); 8905 8906 Cargo.Event.trigger("lightbox_open"); 8907 8908 } 8909 } 8910 } 8911 8912 }, 8913 8914 /** 8915 * Handles the hover over images for the lightbox 8916 * Used the backbone event 8917 */ 8918 LightboxHover : function( e ) { 8919 /* Don't allow this behavior on slideshow thumbs */ 8920 if( $(e.currentTarget).closest('.slideshow_thumbnails').length > 0 || !$(e.currentTarget).attr('src_o') ) { 8921 return false; 8922 } 8923 8924 /* Always verify that we want the lightbox */ 8925 if( Cargo.Model.DisplayOptions.GetLightboxView() ) { 8926 /* Verify there is a larger version available (and is a Cargo image) */ 8927 if( typeof $(e.currentTarget).attr("src_o") != "undefined" ) { 8928 /* Make sure the is no parent <a> */ 8929 if( $(e.currentTarget).parent("a").length == 0 ) { 8930 /* Make the hover */ 8931 if($(e.currentTarget).parent("#freshbox").length == 0) { 8932 /* Normal project view */ 8933 $(e.currentTarget).css("cursor", function() { 8934 if ($.browser.mozilla) { 8935 return '-moz-zoom-in'; 8936 } else if(window.navigator.userAgent.indexOf("Edge") > -1) { 8937 return 'zoom-in'; 8938 } else if ($.browser.webkit) { 8939 return '-webkit-zoom-in'; 8940 } else { 8941 return 'pointer'; 8942 } 8943 }); 8944 } else { 8945 8946 $(e.currentTarget).css("cursor", 'pointer'); 8947 8948 } 8949 } 8950 } 8951 } 8952 }, 8953 8954 /** 8955 * Starts the slideshow inside of the freshbox 8956 * by rendering the freshbox with the current model 8957 * Called by both lightbox click and fullscreen click 8958 */ 8959 InitSlideshow : function(clicked_image) { 8960 8961 var active_container, fullscreenImages, node; 8962 8963 if(Cargo.View.hasOwnProperty('ProjectDetail')){ 8964 // if we're in a project 8965 active_container = Cargo.View.ProjectDetail.$el; 8966 } else { 8967 // if we're not in a project (index on feed based designs) 8968 // check if in slideshow 8969 var slideshowParent = this.clicked_img.closest('.slideshow'); 8970 8971 if(slideshowParent.length > 0){ 8972 active_container = slideshowParent.closest('[data-id]:not(.slideshow)'); 8973 } else { 8974 active_container = this.clicked_img.closest('[data-id]'); 8975 } 8976 } 8977 8978 fullscreenImages = []; 8979 8980 $("img", active_container).each(function() { 8981 // If it's a Cargo uploaded image 8982 if ( $(this).is("[src_o]") && $(this).closest('.slideshow_thumbnails').length === 0 ) { 8983 8984 fullscreenImages.push(this); 8985 8986 } 8987 }); 8988 8989 8990 if(clicked_image === undefined && this.prevent_smart_image_picking !== true) { 8991 8992 var halfScreenHeight = window.innerHeight / 2, 8993 smallestOffset = 9e9; 8994 8995 _.each(fullscreenImages, function(img){ 8996 8997 var rect = img.getBoundingClientRect(), 8998 imgCenter = rect.top + (rect.bottom - rect.top) / 2, 8999 imgCenterOffset = Math.abs(imgCenter - halfScreenHeight); 9000 9001 if(rect.width !== 0 && imgCenterOffset < smallestOffset) { 9002 clicked_image = img; 9003 smallestOffset = imgCenterOffset; 9004 } 9005 9006 }); 9007 9008 } 9009 9010 if(fullscreenImages.length > 0) { 9011 var options = {}; 9012 9013 if(this.isFullscreen) { 9014 options.showAnimationDuration = 0; 9015 } 9016 9017 var gallery = lightbox.openImageArray(fullscreenImages, clicked_image, options); 9018 9019 if(gallery !== undefined && this.isFullscreen) { 9020 9021 gallery.ui.toggleFullscreen(); 9022 9023 } 9024 } 9025 9026 9027 }, 9028 9029 Remove : function() { 9030 // Remove the freshbox 9031 $("#freshbox").remove(); 9032 $("#fullscreen_imgs").remove(); 9033 9034 }, 9035 9036 noop: function(){}, 9037 9038 InitKeyboardNav : function() { 9039 9040 this.DestroyKeyboardNav(); 9041 9042 Cargo.Core.KeyboardShortcut.Add("Up", 38, this.noop); 9043 Cargo.Core.KeyboardShortcut.Add("Down", 36, this.noop); 9044 Cargo.Core.KeyboardShortcut.Add("Left", 37, this.noop); 9045 Cargo.Core.KeyboardShortcut.Add("Right", 39, this.noop); 9046 Cargo.Core.KeyboardShortcut.Add("Esc", 2, this.noop); 9047 9048 }, 9049 9050 DestroyKeyboardNav : function() { 9051 Cargo.Core.KeyboardShortcut.Remove("Up"); 9052 Cargo.Core.KeyboardShortcut.Remove("Down"); 9053 Cargo.Core.KeyboardShortcut.Remove("Left"); 9054 Cargo.Core.KeyboardShortcut.Remove("Right"); 9055 Cargo.Core.KeyboardShortcut.Remove("Esc"); 9056 } 9057 9058 }); 9059 9060/** 9061 * This is a dummy default view 9062 * The purpose of this is to bind to a model / collection and 9063 * This will be loaded by the template loader if there is no view 9064 * already. The reason this may happen is a user adds date-view="something" 9065 * But did not create a view for it. In this case, use this default view 9066 * Which simply renders on reset 9067 */ 9068 Cargo.o.view.Default = Backbone.View.extend({ 9069 9070 initialize: function(options) { 9071 this.options = options; 9072 9073 if( typeof this.collection == "object" ) { 9074 /* Set an event to wait for the model to change */ 9075 this.listenTo(this.collection, "reset", this.render); 9076 this.listenTo(this.collection, "add", this.add); 9077 } 9078 }, 9079 9080 render : function( ) { 9081 9082 /* Make a reference to self */ 9083 var self = this; 9084 9085 /* Get the template for this view (Prevents first load render) */ 9086 var template = Cargo.Template.Get(this.options.template); 9087 9088 /* 9089 * If a template exists, render it 9090 */ 9091 if( template ) { 9092 9093 /* Create a package of data for the render */ 9094 var data_model = Cargo.API.GetDataPackage( this.options.datasetname.toLowerCase(), this.collection.toJSON() ); 9095 9096 /* Render the template and get the markup */ 9097 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 9098 9099 /* Empty the container */ 9100 this.$el.empty(); 9101 this.$el.append( markup ); 9102 9103 } // end template 9104 9105 9106 }, 9107 9108 /** 9109 * The add event is for adding a single item into the collection 9110 * this is intended for autopagination 9111 */ 9112 add : function( model ) { 9113 /* Get the template for this view */ 9114 var template = Cargo.Template.Get(this.options.template); 9115 var self = this; 9116 var in_thumb = ($("[href='"+model.get('url')+"']", self.$el).length > 0); 9117 var in_data = ($("[data-id='"+model.get('id')+"']", self.$el).length > 0); 9118 9119 /* Verify that this model is not already onscreen */ 9120 if( template && !in_thumb && !in_data ) { 9121 /* Create a package of data for the render */ 9122 var data_model = Cargo.API.GetDataPackage( this.options.datasetname.toLowerCase(), model.toJSON() ); 9123 9124 /* Render the template and get the markup */ 9125 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 9126 9127 /* Prepend this to the autopagination handle */ 9128 this.$el.append( markup ); 9129 } 9130 } 9131 }); 9132 9133 9134 Cargo.o.view.Rail = Backbone.View.extend({ 9135 9136 initialize: function() { 9137 /* 9138 * Only do this when not in the admin 9139 */ 9140 if( !Cargo.Helper.IsAdminEdit() ) { 9141 /* Set an event to wait for the model to change */ 9142 this.collection.bind("change", this.render, this); 9143 9144 /* Load in the CSS dependancy */ 9145 var dependencies = [ 9146 "/_jsapps/rail/rail.css" 9147 ]; 9148 9149 $.loadFiles(dependencies).done(function() { 9150 // Done loading the CSS 9151 }); 9152 } 9153 9154 }, 9155 9156 9157 9158 render : function() { 9159 var templates = this.collection.GetTemplates(); 9160 var models = this.collection.GetModels(); 9161 9162 /* Write to the page */ 9163 this.$el.append(templates.masterview_tpl); 9164 9165 this.$el.append(this.collection.GetPopup()); 9166 9167 /* Preload the templates and model */ 9168 Cargo.app.Rail.Templates = templates; 9169 Cargo.app.Rail.Model.LoadModels(models); 9170 9171 $("#rail_wrapper").addClass("api"); 9172 9173 Cargo.Rail.Init(); 9174 Cargo.View.Following.Init(); 9175 } 9176 9177 }); 9178 9179/** 9180 * This is the following toolset that loads in the top left 9181 * Depending on what your login status is and where the request 9182 * is coming from, will determine which toolset will show up 9183 * but that is up to PHP 9184 * 9185 * This is done using the getJSON instead of a backbone model 9186 * because of the need to use JSONP, which I don't believe backbone 9187 * can do. 9188 * 9189 * This handles all of the click events for the view as well 9190 * 9191 * 9192 */ 9193 Cargo.o.view.Following = Backbone.View.extend({ 9194 9195 initialize: function() { 9196 this.Data = { 9197 mouse_down : false, 9198 tool_over : false, 9199 mouse_down : false, 9200 mouse_down : false 9201 }; 9202 9203 var self = this; 9204 9205 // Don't do this if we are previewing or editing 9206 if(Cargo.API.Config.preview && !Cargo.Helper.IsAdminEdit()) { 9207 return false;
9208 } 9209 9210 $.getJSON( 9211 "//"+Cargo.Helper.GetBaseCargoDomain()+"/dispatch/cargosite/cargoToolset/"+Cargo.Helper.GetDomainOrUrl()+"?callback=?", 9212 function(data) { 9213 if( data.error == "nocookie" || Cargo.InspectorLoading === true) { 9214 /* No cookie found, or we are inspecting */ 9215 return; 9216 } else { 9217 if( data.jdata.url ) { 9218 if( Cargo.API.Config.is_domain ) { 9219 $.post( 9220 "/dispatch/cargosite/cargoToolsetLocal", 9221 { token : data.jdata.token, url : data.jdata.url, muid : data.jdata.muid, cv : data.jdata.cv }, 9222 function(dataObj) { 9223 if($("#follow_popup").length == 0) { 9224 self.$el.append(dataObj.jdata.html); 9225 } else { 9226 $("#follow_popup").replaceWith(dataObj.jdata.html); 9227 } 9228 9229 // Init 9230 self.InitToolset(); 9231 self.Init(); 9232 }, 9233 "json"); 9234 } 9235 } else { 9236 // Init 9237 self.InitToolset(); 9238 self.Init(); 9239 } 9240 9241 if( data.html ) { 9242 self.$el.prepend( data.html ); 9243 9244 self.InitToolset(); 9245 self.Init(); 9246 } 9247 9248 } 9249 } 9250 ); 9251 }, 9252 9253 /* 9254 * After the toolset is loaded, do this 9255 */ 9256 InitToolset : function() { 9257 /* Fetch the Rail data */ 9258 Cargo.Model.Rail.fetch({reset: true}); 9259 9260 var self = this; 9261 9262 /* Toolset click */ 9263 $("#toolset_admin").mousedown(function(e) { 9264 self.Data.mouse_down = true; 9265 if(e.button == 2){ 9266 self.ShowContextMenu(); 9267 } else{ 9268 setTimeout("Cargo.View.Following.ToolsetToggle('off')", 500); 9269 } 9270 9271 }).mouseup(function() { 9272 self.Data.mouse_down = false; 9273 9274 }).mouseover(function() { 9275 self.Data.tool_over = true; 9276 9277 }).mouseout(function() { 9278 self.Data.tool_over = false; 9279 9280 }).click(function() { 9281 if ($("#toolset_admin a").attr("href").length > 0 && self.Data.tool_over) { 9282 document.location.href = $("#toolset_admin a").attr("href"); 9283 self.HideContextMenu(); 9284 } 9285 return false; 9286 }); 9287 9288 $("#toolset_admin").bind("contextmenu", function(e) { 9289 return false; 9290 }); 9291 9292 $(document).click(function() { 9293 if(!self.Data.mouse_down && !self.Data.tool_over){ 9294 self.HideContextMenu(); 9295 } 9296 9297 }).mouseup(function(e) { 9298 if(e.button == 2){ 9299 self.Data.mouse_down = false; 9300 } 9301 setTimeout("Cargo.View.Following.ToolsetToggle('on')", 100); 9302 }); 9303 9304 $("#toolset_menu a").bind("click mouseup mouseover", function(e) { 9305 if(e.type == "mouseover") { 9306 self.ToolsetToggle("on"); 9307 } else { 9308 // window.location.href = $(this).attr("href"); 9309 self.HideContextMenu(); 9310 } 9311 }); 9312 9313 $("#toolset_admin a, #toolset_menu a:first").click(function(e) { 9314 if(e.ctrlKey) { 9315 self.ShowContextMenu(); 9316 return false; 9317 } else if(typeof require == "function") { 9318 Cargo.Event.trigger("add_history", "admin"); 9319 return false; 9320 } 9321 }); 9322 9323 Cargo.Event.on("project_load_complete", function( pid ) { 9324 self.HideContextMenu(); 9325 }); 9326 9327 $("#toolset_menu a:first").addClass("toolset_first"); 9328 $("#toolset_menu a:last").addClass("toolset_last"); 9329 9330 Cargo.app.Rail.Events.trigger("toolset:loaded"); 9331 9332 }, 9333 9334 /* 9335 * Hide the content menu 9336 */ 9337 HideContextMenu : function() { 9338 $("#toolset_admin").removeClass("toolset_active"); 9339 $("#toolset_menu").hide(); 9340 9341 Cargo.View.Following.ToolsetToggle("on"); 9342 }, 9343 9344 /* 9345 * Show the context menu 9346 */ 9347 ShowContextMenu : function(which) { 9348 var toolset_top = $("#toolset").css("top"); 9349 var toolset_right = $("#toolset").css("right"); 9350 $("#toolset_menu").css({"top" : toolset_top , "right" : toolset_right}); 9351 $("#toolset_admin").addClass("toolset_active"); 9352 $("#toolset_menu").show(); 9353 $("#css_inspector_trigger").on("click", function() { 9354 loadInspector(); 9355 return false; 9356 }); 9357 }, 9358 9359 /* 9360 * Toggle the toolset menu 9361 */ 9362 ToolsetToggle : function(which) { 9363 if (which == "on") { 9364 if ($("#toolset_admin a").length > 0 && $("#toolset_admin a").attr("rel") != "undefined") { 9365 $("#toolset_admin a").attr("href",$("#toolset_admin a").attr("rel")).removeAttr("rel"); 9366 } 9367 } else if (which == "off") { 9368 if ($("#toolset_admin a").length > 0 && $("#toolset_admin a").attr("href") != "undefined" && tool_over && mouse_down) { 9369 $("#toolset_admin a").attr("rel",$("#toolset_admin a").attr("href")).removeAttr("href"); 9370 mouse_down = false;
9371 Cargo.View.Following.ShowContextMenu(); 9372 } 9373 } 9374 }, 9375 9376 /** 9377 * bind all follow/unfollow links 9378 */ 9379 Init : function() { 9380 9381 $("a[rel='follow']").off('click').on('click', function() { 9382 Cargo.View.Following.ShowFollowWindow($(this)); 9383 return false; 9384 }); 9385 9386 $(".follow_popup_ok").off('click').on('click', function() { 9387 Cargo.View.Following.DoFollow($(this).attr('data-my_uid'), $(this).attr('data-my_cv'), $(this).attr('data-their_uid'), $(this).attr('data-type')); 9388 return false; 9389 }); 9390 9391 $(".follow_popup_cancel").off('click').on('click', function() { 9392 Cargo.View.Following.Hide(); 9393 return false; 9394 }); 9395 9396 }, 9397 9398 Show : function() { 9399 $("#follow_popup").show(); 9400 Cargo.Core.KeyboardShortcut.Add("Return", 13, function() { 9401 $("a.follow_popup_ok").click(); 9402 }); 9403 9404 }, 9405 9406 Hide : function() { 9407 9408 // Hide the element 9409 $("#follow_popup").hide(); 9410 9411 // Remove the keys 9412 Cargo.Core.KeyboardShortcut.Remove("Return"); 9413 9414 if(typeof Cargo.Home == "object") { 9415 Cargo.Home.Personal.AddEnterShortcut(); 9416 } 9417 9418 }, 9419 9420 9421 ShowFollowWindow : function(el) { 9422 Cargo.View.Following.Show(); 9423 9424 $("#follow_popup_note").html(ucFirst($(el).attr("data-type"))+" "+$(el).attr('data-displayurl')+"?"); 9425 $(".follow_popup_ok").attr({ 9426 'data-their_uid': $(el).attr("data-their_uid"), 9427 'data-my_uid' : $(el).attr("data-my_uid"), 9428 'data-my_cv' : $(el).attr("data-my_cv"), 9429 'data-type' : $(el).attr("data-type") 9430 }); 9431 Cargo.View.Following.Data.el = el; 9432 9433 }, 9434 9435 DoFollow : function(my_uid, my_cv, their_uid, type) { 9436 $("#following_loadspin").show(); 9437 $.post( 9438 "/dispatch/user/doFollow", 9439 { my_uid:my_uid, my_cv:my_cv, their_uid:their_uid, type:type }, 9440 Cargo.View.Following.FollowResult, 9441 "json" 9442 ); 9443 9444 }, 9445 9446 FollowResult : function(dataObj) { 9447 $("#following_loadspin").hide(); 9448 9449 if(typeof Cargo.Home == "object") { 9450 Cargo.Home.Personal.hideProjectAfterUnfollow(Cargo.View.Following.Data.el); 9451 9452 } else if(Cargo.View.Following.Data.el.attr("id")) { 9453 $("#follow_links").html(dataObj.html); 9454 $("#rail_follow").html(dataObj.html); 9455 9456 // update "template" 9457 if(Cargo.app.Rail.view.following) { 9458 Cargo.app.Rail.Templates.following_tpl = Cargo.app.Rail.view.following.el.innerHTML; 9459 } 9460 9461 } else { 9462 Cargo.View.Following.Data.el.replaceWith(dataObj.jdata.tag_html); 9463 } 9464 9465 Cargo.View.Following.Hide(); 9466 } 9467 9468 9469 9470 }); 9471 9472/** 9473 * search 9474 */ 9475Cargo.o.view.Search = Backbone.View.extend({ 9476 events: { 9477 'keypress input[type=text]' : 'submitOnEnter', 9478 'focus input[type=text]' : 'focus', 9479 'blur input[type=text]' : 'blur' 9480 }, 9481 9482 initialize: function() { 9483 var rel = $("input[type=text]", this.$el).attr("rel"); 9484 if(rel !== undefined){ 9485 if($("input[type=text]", this.$el).val() == "" && rel.length > 0) { 9486 $("input[type=text]", this.$el).val( rel ); 9487 } 9488 } 9489 9490 // If this is the first load of search, load everything 9491 if(this.$el.hasClass('search') || $('input#results_search_term', this.$el).length > 0) { 9492 Cargo.Collection.Search = new Cargo.o.collection.search(); 9493 Cargo.Model.DisplayOptions.set('pagination_count', 9999); 9494 var _this = this; 9495 Cargo.Collection.Search.fetch({reset: true, complete: function(data) { 9496 _this.render(); 9497 }}); 9498 } 9499 }, 9500 9501 render : function( ) { 9502 var template = Cargo.Template.Get('Search'); 9503 var data_model = Cargo.API.GetDataPackage('search', Cargo.Collection.Search.toJSON()); 9504 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 9505 9506 this.$el.empty(); 9507 this.$el.html( markup ); 9508 9509 $('input#results_search_term', this.$el).val(Cargo.API.Config.search_term); 9510 }, 9511 9512 focus : function(e) { 9513 var t = e.currentTarget; 9514 if($(t).val() == $(t).attr("rel")) { 9515 $(t).val(""); 9516 } 9517 }, 9518 9519 blur : function(e) { 9520 var t = e.currentTarget; 9521 if($(t).val() == "") { 9522 $(t).val( $(t).attr("rel") ); 9523 } 9524 }, 9525 9526 submitOnEnter: function(e) { 9527 if (e.keyCode != 13) return; 9528 var t = e.currentTarget; 9529 Cargo.Helper.GoToUrl('search/' + $(t).val()); 9530 }, 9531 9532 9533}); 9534 9535/** 9536 * This is a view for sets. It is identical to the default view 9537 */ 9538 Cargo.o.view.Sets = Backbone.View.extend({ 9539 9540 initialize: function(options) { 9541 this.options = options; 9542 9543 if( typeof this.collection == "object" ) { 9544 /* Set an event to wait for the model to change */ 9545 this.listenTo(this.collection, "reset", this.render); 9546 this.listenTo(this.collection, "add", this.add); 9547 9548 this.collection.fetch({ reset : true }); 9549 } 9550 }, 9551 9552 render : function( ) { 9553 9554 /* Make a reference to self */ 9555 var self = this; 9556 9557 /* Get the template for this view (Prevents first load render) */ 9558 var template = Cargo.Template.Get("Sets"); 9559 9560 /* 9561 * If a template exists, render it 9562 */ 9563 if( template ) { 9564 9565 var dataset = (this.options.datasetname) ? this.options.datasetname.toLowerCase() : "sets"; 9566 9567 /* Create a package of data for the render */ 9568 var data_model = Cargo.API.GetDataPackage( dataset, this.collection.toJSON() ); 9569 9570 /* Render the template and get the markup */ 9571 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 9572 9573 /* Empty the container */ 9574 this.$el.empty(); 9575 this.$el.append( markup ); 9576 9577 } // end template 9578 9579 9580 }, 9581 9582 /** 9583 * The add event is for adding a single item into the collection 9584 * this is intended for autopagination 9585 */ 9586 add : function( model ) { 9587 /* Get the template for this view */ 9588 var template = Cargo.Template.Get(this.options.template); 9589 var self = this; 9590 var in_thumb = ($("[href='"+model.get('url')+"']", self.$el).length > 0); 9591 var in_data = ($("[data-id='"+model.get('id')+"']", self.$el).length > 0); 9592 9593 /* Verify that this model is not already onscreen */ 9594 if( template && !in_thumb && !in_data ) { 9595 /* Create a package of data for the render */ 9596 var data_model = Cargo.API.GetDataPackage( this.options.datasetname.toLowerCase(), model.toJSON() ); 9597 9598 /* Render the template and get the markup */ 9599 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 9600 9601 /* Prepend this to the autopagination handle */ 9602 this.$el.append( markup ); 9603 } 9604 } 9605 }); 9606 9607/** 9608 * 9609 */ 9610 Cargo.o.view.Header = Backbone.View.extend({ 9611 9612 initialize: function(options) { 9613 this.options = options; 9614 9615 this.listenTo(this.collection, "change", this.render); 9616 }, 9617 9618 render : function( ) { 9619 /* Make a reference to self */ 9620 var self = this; 9621 9622 /* Get the template for this view (Prevents first load render) */ 9623 var template = Cargo.Template.Get("Header"); 9624 9625 /* 9626 * If a template exists, render it 9627 */ 9628 if( template ) { 9629 9630 /* Create a package of data for the render */ 9631 var data_model = Cargo.API.GetDataPackage(); 9632 9633 /* Render the template and get the markup */ 9634 var markup = Cargo.Core.Handlebars.Render( template, data_model ); 9635 9636 /* Empty the container */ 9637 this.$el.empty(); 9638 this.$el.append( markup ); 9639 9640 } // end template 9641 9642 9643 }, 9644 9645 }); 9646 9647/*************************************************************************** 9648 * 9649 * Initialize the application 9650 * Find template stubs and load them
9651 * Start the router 9652 * 9653 **************************************************************************/ 9654 9655/* 9656 * Start this application 9657 * Initialize all objects 9658 * Start the router 9659 */ 9660 9661$(function() { 9662 9663 /* 9664 * Default data storage 9665 */ 9666 Cargo.Data = { }; 9667 9668 /* 9669 * Init all the backbone things 9670 */ 9671 Cargo.API = new Cargo.o.api(); 9672 Cargo.Helper = new Cargo.o.helpers(); 9673 Cargo.Template = new Cargo.o.templates(); 9674 Cargo.Router = new Cargo.o.router(); 9675 Cargo.Analytics = new Cargo.o.analytics(); 9676 9677 /* 9678 * Create a base class for all the models 9679 */ 9680 Cargo.Model.BaseProject = Cargo.o.model.project; 9681 Cargo.Model.Project = new Cargo.Model.BaseProject(); 9682 Cargo.Model.DisplayOptions = new Cargo.o.model.display_options(); 9683 Cargo.Model.Site = new Cargo.o.model.site(); 9684 Cargo.Model.Rail = new Cargo.o.model.rail(); 9685 Cargo.Model.Asset = new Cargo.o.model.asset(); 9686 Cargo.Model.Images = new Cargo.o.model.images(); 9687 Cargo.Model.Default = new Cargo.o.model.undefined_default(); 9688 9689 /* 9690 * Set the first collection of projects and navigation 9691 */ 9692 Cargo.Collection.Projects = new Cargo.o.collection.projects({ 9693 model : Cargo.Model.BaseProject 9694 }); 9695 9696 Cargo.Collection.Pages = new Cargo.o.collection.pages({ 9697 model : Cargo.Model.BaseProject 9698 }); 9699 9700 Cargo.Collection.Navigation = new Cargo.o.collection.navigation({ 9701 model : Cargo.Model.BaseProject 9702 }); 9703 9704 Cargo.Collection.Sets = new Cargo.o.collection.sets({ 9705 model : Cargo.Model.BaseProject 9706 }); 9707 9708 Cargo.Collection.Assets = new Cargo.o.collection.assets({ 9709 model : Cargo.Model.Asset 9710 }); 9711 9712 Cargo.Collection.Images = new Cargo.o.collection.images({ 9713 model : Cargo.Model.Images 9714 }); 9715 9716 Cargo.Collection.Default = new Cargo.o.collection.undefined_default({ 9717 model : Cargo.Model.Default 9718 }); 9719 9720 /* 9721 * Bind the reset event on the project collection 9722 */ 9723 Cargo.Collection.Projects.on('reset', function(collection) { 9724 Cargo.Event.trigger("project_collection_reset"); 9725 }); 9726 9727 /* 9728 * Fetch the default data first (pre-populate the page) 9729 */ 9730 9731 Cargo.Model.DisplayOptions.once('change', function(){ 9732 9733 Cargo.Event.trigger('display_options_available', Cargo.Model.DisplayOptions); 9734 9735 }); 9736 9737 Cargo.Model.DisplayOptions.fetch({reset: true}); 9738 Cargo.Model.Project.fetch({reset: true}); 9739 Cargo.Collection.Assets.fetch(); 9740 Cargo.Collection.Pages.fetch(); 9741 Cargo.Model.Site.fetch(); 9742 9743 // Check to see if there is a wrong URL case and redirect 9744 Cargo.Helper.TestUrlCaseAndRedirect(); 9745 9746 /* 9747 * Make sure the main view has been init 9748 */ 9749 if( typeof(Cargo.View.Main) == "undefined" ) { 9750 Cargo.View.Main = new Cargo.o.view.Main({ 9751 el : "body", 9752 collection : Cargo.Model.Site 9753 }); 9754 } 9755 9756 /* 9757 * Finds all of the view stubs in the DOM 9758 */ 9759 Cargo.Template.FindAndAddViewStubs(); 9760 9761 /* 9762 * Init all found views 9763 */ 9764 Cargo.Template.LoadFoundViews(); 9765 9766 /* 9767 * Fetch the data 9768 * But delay project data if the site was just created 9769 */ 9770 if(Cargo.Helper.GetRouteName() != "search") { 9771 Cargo.Collection.Projects.fetch({ 9772 complete: function() { 9773 Cargo.Event.trigger("project_collection_reset"); 9774 Cargo.Event.trigger("first_project_collection_reset"); 9775 } 9776 }); 9777 } 9778 9779 /* 9780 * Fetch the navigation data and trigger a reset event 9781 */ 9782 Cargo.Collection.Navigation.fetch({ 9783 complete: function() { 9784 Cargo.Event.trigger("first_navigation_collection_reset"); 9785 } 9786 }); 9787 9788 /* 9789 * Start the router, if we should 9790 */ 9791 if( Cargo.Helper.IsFeedDesign() && Cargo.Helper.isOldIE() ) { 9792 // Do not start history 9793 } else { 9794 // Start backbone history 9795 Backbone.history.start({ 9796 pushState: true, 9797 root: Cargo.Helper.GetHomeUrl(true) 9798 }); 9799 9800 // Trigger set loaded event 9801 if(Cargo.API.Config.set) { 9802 Cargo.Event.trigger("set_loaded", Cargo.API.Config.set); 9803 } 9804 } 9805 9806 /* 9807 * Init the default keyboard shortcuts 9808 */ 9809 Cargo.Core.KeyboardShortcut.InitDefaults(); 9810 9811 /* 9812 * Dig through the index and find any stubs to add views 9813 * This will load the templates into memory 9814 */ 9815 Cargo.Template.LoadAll(); 9816 9817 /* 9818 * Init all found views (again) 9819 * This time, it is taking into account the design 9820 */ 9821 Cargo.Template.LoadFoundViews(); 9822 9823 9824 /* 9825 * Check and init freshbox 9826 * Must be here because it requires text.js 9827 */ 9828 Cargo.Core.CheckAndInitFreshbox(); 9829 9830 /* 9831 * Sniff for inspector 9832 */ 9833 if(location.search.replace("?","") == "inspector") { 9834 Cargo.Core.LoadInspector(); 9835 } 9836 9837 /* 9838 * Activate the hi-res images if needed 9839 */ 9840 Cargo.Core.HiRes.Loader.init(); 9841 9842 /* Common events that are fired multiple times */ 9843 Cargo.Event.Common(); 9844 9845 /* Look for a search form in a project or on the page */ 9846 Cargo.Core.FindAndLoadSearch(true); 9847 9848 /* Look for a meta tag with the EU cookie notification */ 9849 Cargo.Core.EU_cookie_notification(); 9850 9851 9852}); 9853 9854/*************************************************************************** 9855 * 9856 * The Rail app is the controller of the right rail housing 9857 * following, tracemarks and other things 9858 * 9859 * This is meant to be agnostic to Cargo 1.0 or 2.0 9860 * Uses Backbone and Handlebars 9861 * 9862 * Assumes the Cargo namespace exists 9863 * Sets the Cargo.app namepsace if one does not exist 9864 * 9865 **************************************************************************/ 9866Cargo.app = window.Cargo.app || { }; 9867Cargo.app.Rail = { }; 9868 9869/* Create the default objects */ 9870Cargo.app.Rail.Model = { }; // All models base 9871Cargo.app.Rail.Collection = { }; // All collections base 9872Cargo.app.Rail.View = { }; // All views base 9873Cargo.app.Rail.view = { }; // instanciated views 9874Cargo.app.Rail.Events = { }; // All events base 9875Cargo.app.Rail.Templates = { }; // All templates (these are loaded on init) 9876 9877 9878/*************************************************************************** 9879 * 9880 * Models 9881 * 9882 **************************************************************************/ 9883 9884/** 9885 * This is the init and loads all models that are passed 9886 * Expects an array of models as name => data 9887 * name = "masterview_model" 9888 * Create the model name for this application like "MasterviewModel" 9889 */ 9890Cargo.app.Rail.Model.LoadModels = function( models ) { 9891 9892 _( models ).each(function( value, key ) { 9893 var model_name = ucFirst(key.split("_")[0]) + ucFirst(key.split("_")[1]) + ""; 9894 9895 /* Create a new Backbone model based on the data coming in */ 9896 Cargo.app.Rail.Model[model_name] = new Backbone.Model( value ); 9897 9898 }); 9899}; 9900 9901 9902/*************************************************************************** 9903 * 9904 * Events 9905 * 9906 **************************************************************************/ 9907 9908/* Init the events */ 9909Cargo.app.Rail.Events = _.extend({}, Backbone.Events); 9910 9911 9912/** 9913 * An event for showing the following 9914 * This is triggered when the following icon is clicked 9915 */ 9916Cargo.app.Rail.Events.on("show_rail_following", function() { 9917 9918 /* Make sure to destroy old versions */ 9919 Cargo.app.Rail.Events.trigger("following:destroy"); 9920 9921 /* Init a new version of this view */ 9922 Cargo.app.Rail.view.following = new Cargo.app.Rail.View.Following({ 9923 el : "#rail_following", 9924 collection: Cargo.app.Rail.Model.FollowingModel 9925 }); 9926 9927}); 9928 9929 9930/* 9931+ * Event for image drag start 9932+ * Init the tracemarks view 9933+ */ 9934Cargo.app.Rail.Events.on("image_drag_start", function() { 9935 9936 /* Make sure to destroy old versions */ 9937 Cargo.app.Rail.Events.trigger("selections:destroy"); 9938 9939 /* Init a new version of this view */ 9940 new Cargo.app.Rail.View.Selections({ 9941 el : "#rail_selections", 9942 collection: Cargo.app.Rail.Model.SelectionsModel 9943 }); 9944 9945}); 9946 9947 9948/*************************************************************************** 9949 * 9950 * Views 9951 * 9952 **************************************************************************/ 9953 9954/************************** 9955 * Master 9956 *************************/ 9957Cargo.app.Rail.View.Master = Backbone.View.extend({ 9958 9959 initialize : function() { 9960 9961 var self = this; 9962 9963 // Set an event to listen for showing the rail 9964 Cargo.app.Rail.Events.bind("rail:show", this.Show, this); 9965 9966 // Set an event to listen for hiding the rail 9967 Cargo.app.Rail.Events.bind("rail:hide", this.Hide, this); 9968 9969 // IE check 9970 if ( $.browser.msie ) { 9971 $("#rail_wrapper").addClass("rail_ie"); 9972 } 9973 9974 new Cargo.app.Rail.View.Screen({ 9975 el : "#rail_screen" 9976 }); 9977 9978 if(Cargo.hasOwnProperty('Core')){ 9979 Cargo.Core.ReplaceLoadingAnims.init(); 9980 } else { 9981 Cargo.ReplaceLoadingAnims(); 9982 } 9983 9984 }, 9985 9986 events : { 9987 9988 }, 9989 9990 render : function() { 9991 9992 },
9993 9994 /** 9995 * Hide the rail and reset the data 9996 */ 9997 Show : function() { 9998 9999 if ( ! $.browser.msie ) { 10000 10001 _.delay(function() { 10002 $("body, html").addClass("rail_active"); 10003 $("#rail_wrapper").addClass("active"); 10004 }, 1); 10005 10006 } else { 10007 10008 $("body, html").addClass("rail_active"); 10009 $("#rail_wrapper").addClass("active"); 10010 10011 } 10012 }, 10013 10014 Hide : function() { 10015 $("body, html").removeClass("rail_active"); 10016 $("#rail_wrapper").attr("class", "api"); 10017 10018 10019 // IE check 10020 if ( $.browser.msie ) { 10021 $("#rail_wrapper").addClass("rail_ie"); 10022 } 10023 10024 // Hide the follow popup 10025 if( typeof Cargo.Follow != "undefined" ) { 10026 Cargo.Follow.Hide(); 10027 } 10028 10029 10030 // Trigger the rail hide event 10031 _.delay(function() { Cargo.app.Rail.Events.trigger("following:hide") }, 300); 10032 } 10033 10034}); 10035 10036/************************** 10037 * Screen 10038 *************************/ 10039Cargo.app.Rail.View.Screen = Backbone.View.extend({ 10040 10041 initialize : function() { 10042 10043 }, 10044 10045 events : { 10046 "click" : "clicked", 10047 "mouseover" : "hovered" 10048 }, 10049 10050 render : function() { 10051 10052 }, 10053 10054 clicked : function() { 10055 Cargo.app.Rail.Events.trigger("rail:hide"); 10056 }, 10057 10058 hovered : function() { 10059 if ( !Cargo.Rail.Data.dragging && 10060 !Cargo.Rail.Data.is_saving && 10061 $("#rail_selections").is(":visible") 10062 ) { 10063 Cargo.app.Rail.Events.trigger("rail:hide"); 10064 } 10065 } 10066 10067}); 10068 10069 10070/************************** 10071 * Following 10072 *************************/ 10073Cargo.app.Rail.View.Following = Backbone.View.extend({ 10074 10075 initialize : function() { 10076 // Set the template 10077 this.template = Cargo.app.Rail.Templates.following_tpl; 10078 10079 // Set an event to wait for the model to change 10080 if(this.collection) { 10081 this.collection.bind("reset", this.render, this); 10082 10083 // Show the rail 10084 Cargo.app.Rail.Events.trigger("rail:show"); 10085 10086 // Create an event for the destroy method 10087 Cargo.app.Rail.Events.bind("following:destroy", this.destroy, this); 10088 10089 // Set an event to listen for hiding the rail 10090 Cargo.app.Rail.Events.bind("following:hide", this.Hide, this); 10091 10092 this.render(); 10093 } 10094 }, 10095 10096 10097 events: { 10098 "click #rail_follow a" : "DoFollow" 10099 }, 10100 10101 render : function() { 10102 10103 /* Empty the container */ 10104 this.$el.empty(); 10105 10106 var markup = Handlebars.compile( this.template )( this.collection ); 10107 this.$el.append( markup ).show(); 10108 10109 // Active state 10110 $("#rail_wrapper").addClass("following_active"); 10111 10112 // Close rail when clicking name 10113 $(".rail_name").click(function() { 10114 Cargo.app.Rail.Events.trigger("rail:hide"); 10115 return false; 10116 }); 10117 10118 var self = this; 10119 this.columns(); 10120 10121 $(window).resize(function() { self.columns() }); 10122 10123 }, 10124 10125 columns : function() { 10126 10127 var width = $(this.$el).width() - 40; 10128 var ratio = Math.floor((width / 120)); 10129 var percent = 100 / ratio; 10130 10131 if ( width < 181 ) { 10132 percent = 100; 10133 } 10134 10135 // $(".rail_module", this.$el).width(percent + "%"); 10136 10137 }, 10138 10139 /* Hide this */ 10140 Hide : function() { 10141 this.$el.hide(); 10142 }, 10143 10144 /** 10145 * Destroy this view and unbind it's events 10146 */ 10147 destroy : function() { 10148 this.undelegateEvents(); 10149 }, 10150 10151 /* 10152 * This does the actual follow / unfollow action 10153 */ 10154 DoFollow : function( e ) { 10155 e.preventDefault(); 10156 10157 var target = e.currentTarget; 10158 var ftype = $(target).attr("data-ftype"); 10159 var ucase_which = ucFirst(ftype); 10160 10161 if(typeof Cargo.Follow == "object") { 10162 Cargo.Follow.ShowFollowWindow($(target)); 10163 10164 } else if(typeof Cargo.View.Following == "object") { 10165 Cargo.View.Following.ShowFollowWindow($(target)); 10166 } 10167 10168 } 10169 10170}); 10171 10172 10173 10174/************************** 10175 * Selections 10176 *************************/ 10177Cargo.app.Rail.View.Selections = Backbone.View.extend({ 10178 10179 last_id_drop : 0, 10180 10181 initialize : function() { 10182 // Set the template 10183 this.template = Cargo.app.Rail.Templates.selections_tpl; 10184 10185 // Set an event to wait for the model to change 10186 this.collection.bind("reset", this.render, this); 10187 10188 // Show the rail 10189 Cargo.app.Rail.Events.trigger("rail:show"); 10190 10191 // Create an event for the destroy method 10192 Cargo.app.Rail.Events.bind("selections:destroy", this.destroy, this); 10193 10194 // Set an event to listen for hiding the rail 10195 Cargo.app.Rail.Events.bind("rail:hide", this.Hide, this); 10196 10197 this.render(); 10198 }, 10199 10200 10201 events: { 10202 "mouseover" : "hovered", 10203 "mouseout" : "hoverout" 10204 }, 10205 10206 hovered : function() { 10207 Cargo.Rail.Data.is_on_screen = false;
10208 }, 10209 10210 hoverout : function() { 10211 Cargo.Rail.Data.is_on_screen = true; 10212 }, 10213 10214 render : function() { 10215 /* Empty the container */ 10216 this.$el.empty(); 10217 10218 var markup = Handlebars.compile( this.template )( this.collection ); 10219 this.$el.append( markup ).show(); 10220 10221 this.SetDropZone(); 10222 10223 $("#rail_wrapper").addClass("selection_active"); 10224 }, 10225 10226 SetDropZone : function() { 10227 var self = this; 10228 10229 $("#rail_screen").droppable( 10230 '*', 10231 // Drag enter 10232 function(e) { }, 10233 // Drag leave 10234 function() { }, 10235 // Drop! 10236 function(e) { 10237 Cargo.app.Rail.Events.trigger("rail:hide"); 10238 } 10239 ); 10240 10241 $("#selections_login").droppable( 10242 '*', 10243 // Drag enter 10244 function(e) { }, 10245 // Drag leave 10246 function() { }, 10247 // Drop! 10248 function(e) { 10249 Cargo.Rail.Data.dragging = false; 10250 } 10251 ); 10252 10253 $("#rail_droplet").droppable( 10254 // Accepts 10255 '*', 10256 10257 // Drag enter 10258 function(e) { 10259 $(this).addClass("active"); 10260 Cargo.Rail.Data.is_on_screen = false; 10261 }, 10262 10263 // Drag leave 10264 function() { 10265 $(this).removeClass("active"); 10266 }, 10267 10268 // Drop! 10269 function(e) { 10270 self.HandleImageDrop(); 10271 } 10272 10273 ); // end drop 10274 10275 }, 10276 10277 HandleImageDrop : function() { 10278 Cargo.Rail.Data.is_saving = true; 10279 10280 var id = this.collection.get("mid"); 10281 // don't allow for the image in the rail to be dropped again 10282 if(this.last_id_drop == id) { 10283 return; 10284 } 10285 10286 this.last_id_drop = id; 10287 10288 $("#rail_save").addClass("active"); 10289 $(".rail_add").addClass("hidden"); 10290 10291 $("#rail_preview").addClass("selection_dropped"); 10292 $("#rail_preview").attr("data-id", id); 10293 10294 /* 10295 * Rail Preview 10296 */ 10297 $("#rail_preview img:not(.rail_preview_spinner)").attr("src", this.collection.get("src")); 10298 $("#rail_preview .rail_preview_spinner").css("top", ( $("#rail_preview img:not(.rail_preview_spinner)").height() / 2 ) + 40 + "px"); 10299 10300 if( typeof Cargo.ReplaceLoadingAnims == "function") { 10301 Cargo.ReplaceLoadingAnims(); 10302 } else { 10303 Cargo.Core.ReplaceLoadingAnims.init(); 10304 } 10305 10306 // Save the image 10307 var cuid = ""; 10308 10309 if(typeof Cargo.Helper == "undefined"){ 10310 cuid = Cargo.Config.GetCookieUid(); 10311 } else { 10312 cuid = Cargo.Helper.GetUrl(); 10313 } 10314 10315 $.post("/dispatch/selections/selection", { mid : this.collection.get("mid"), cuid : cuid }, function(server_data) { 10316 if(server_data.error) { 10317 $(".rail_preview_spinner").hide(); 10318 alert(server_data.message); 10319 Cargo.app.Rail.Events.trigger("rail:hide"); 10320 10321 } else { 10322 10323 // Remove the fake spinner 10324 _.delay(function() { 10325 $("#rail_preview[data-id=" + id + "]").addClass("selection_sent"); 10326 }, 600); 10327 10328 _.delay(function() { 10329 if(Cargo.Rail.Data.is_on_screen) { 10330 Cargo.app.Rail.Events.trigger("rail:hide"); 10331 } 10332 Cargo.Rail.Data.is_saving = false; 10333 }, 1000); 10334 10335 } 10336 }); 10337 10338 // Tell the app the dragging has stopped, but give it a beat 10339 _.delay(function() { Cargo.Rail.Data.dragging = false; }, 500); 10340 10341 }, 10342 10343 10344 10345 /* Hide this */ 10346 Hide : function() { 10347 this.last_id_drop = 0; 10348 this.$el.hide(); 10349 }, 10350 10351 /** 10352 * Destroy this view and unbind it's events 10353 */ 10354 destroy : function() { 10355 this.undelegateEvents(); 10356 } 10357 10358 10359}); 10360 10361 10362 10363 10364 10365 10366/* 10367 * Start this backbone application 10368 * Set the jquery dragging 10369 * Hijack the following icon click 10370 */ 10371Cargo.Rail = { 10372 10373 Data : { 10374 "dragging" : false, 10375 "is_saving" : false, 10376 "is_on_screen" : false 10377 }, 10378 10379 Init : function() { 10380 10381 /* 10382 * Set the backbone view for the master 10383 */ 10384 new Cargo.app.Rail.View.Master({ 10385 el : "#rail_wrapper" 10386 }); 10387 10388 /* 10389 * Following icon 10390 */ 10391 $("#toolset_follow_pair a, #toolset_follow a").click(function(e) { 10392 e.preventDefault(); 10393 Cargo.app.Rail.Events.trigger("show_rail_following"); 10394 return false;
10395 }); 10396 10397 /* 10398 * Dragging and dropping 10399 */ 10400 var setDrag = function() { 10401 // Disallow on mobile 10402 var is_mobile = (typeof Cargo.Helper == "object") ? Cargo.Helper.isMobile() : Cargo.Config.isMobile(); 10403 if(is_mobile) { 10404 return false; 10405 } 10406 10407 // Firefox/IE anchor img slideshow 10408 if ( $.browser.mozilla || $.browser.msie ) { 10409 $(".project_content a:has(img[data-mid]), #container a:has(img[data-mid])").dragstart(function(e) { 10410 Cargo.Rail.Drag($("img:visible", this)); 10411 }); 10412 } 10413 10414 // Configure the drag for all other images 10415 $("img[data-mid]").dragstart(function(e) { 10416 // console.log("Dragging starting"); 10417 Cargo.Rail.Drag($(this)); 10418 10419 }).dragend(function(e) { 10420 10421 // Delay unless in webkit 10422 if ( $.browser.mozilla || $.browser.msie ) { 10423 setTimeout(function() { Cargo.Rail.Data.dragging = false }, 100); 10424 } else { 10425 Cargo.Rail.Data.dragging = false 10426 } 10427 10428 }); 10429 } 10430 10431 /** 10432 * Bind to events 10433 */ 10434 10435 $(document).bind("projectLoadComplete", function(e, pid) { 10436 Cargo.app.Rail.Events.trigger("rail:hide"); 10437 setDrag(); 10438 }); 10439 10440 $(document).bind("projectLoadComplete", function(e, pid) { 10441 setDrag(); 10442 }); 10443 10444 $(document).bind("projectIndex", function(e, pid) { 10445 Cargo.app.Rail.Events.trigger("rail:hide"); 10446 }); 10447 10448 $(document).bind("paginationComplete", function(e, data) { 10449 setDrag(); 10450 }); 10451 10452 setDrag(); 10453 }, 10454 10455 Drag : function(element) { 10456 10457 Cargo.Rail.Data.dragging = true; 10458 10459 Cargo.app.Rail.Events.trigger("image_drag_start"); 10460 10461 // Set the value of the dragged element in the model 10462 Cargo.app.Rail.Model.SelectionsModel.set({ 10463 src : $(element).attr('src'), 10464 mid : $(element).attr('data-mid') 10465 }); 10466 10467 } 10468 10469}; 10470 10471/*! cargoEditor 05-05-2015 */ 10472!function(){var a=function(a,b,c){return a.prototype.hasOwnProperty(name)!==!1?!1:void(a.prototype[b]=c)};a(Node,"saveable",!1),a(Node,"setSaveable",function(a){return this.saveable=a,this}),a(Node,"isSaveable",function(){return this.saveable}),Node.prototype.persistCloneNode=function(){var a=function(b,c,d){for(var e=0;e<b.childNodes.length;e++)d(b.childNodes[e],c.childNodes[e]),b.childNodes[e].childNodes.length>0&&a(b.childNodes[e],c.childNodes[e],d)},b=Node.prototype.cloneNode.apply(this,arguments);return this.isSaveable()===!0&&b.setSaveable(!0),this.childNodes.length>0&&a(this,b,function(a,b){a.isSaveable()===!0&&b.setSaveable(!0)}),b},this.core={markAsEditable:function(){}}}(window.CargoEditor=window.CargoEditor||{}); 10473
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.