/*global define */ define(['jquery', 'underscore', 'backbone', 'views/AltHeaderView', 'views/NavbarView', 'views/FooterView', 'text!templates/alert.html', 'text!templates/appHead.html', 'text!templates/app.html', 'text!templates/loading.html' ], function($, _, Backbone, AltHeaderView, NavbarView, FooterView, AlertTemplate, AppHeadTemplate, AppTemplate, LoadingTemplate) { 'use strict'; var app = app || {}; var theme = document.getElementById("loader").getAttribute("data-theme"); // Our overall **AppView** is the top-level piece of UI. var AppView = Backbone.View.extend({ // Instead of generating a new element, bind to the existing skeleton of // the App already present in the HTML. el: '#metacatui-app', //Templates template: _.template(AppTemplate), alertTemplate: _.template(AlertTemplate), appHeadTemplate: _.template(AppHeadTemplate), loadingTemplate: _.template(LoadingTemplate), events: { "click" : "closePopovers", 'click .btn.direct-search' : 'routeToMetadata', 'keypress input.direct-search' : 'routeToMetadataOnEnter', "click .toggle-slide" : "toggleSlide", "click input.copy" : "higlightInput", "focus input.copy" : "higlightInput", "click textarea.copy" : "higlightInput", "focus textarea.copy" : "higlightInput", "click .open-chat" : "openChatWithMessage", "click .login.redirect" : "sendToLogin", "focus .jump-width-input" : "widenInput", "focusout .jump-width-input" : "narrowInput" }, initialize: function () { if(window.location.search.indexOf("error=Unable%20to%20authenticate%20LDAP%20user") > -1){ window.location = window.location.origin + window.location.pathname + "#signinldaperror"; } //Is there a logged-in user? appUserModel.checkStatus(); // set up the head - make sure to prepend, otherwise the CSS may be out of order! $("head").prepend(this.appHeadTemplate({ theme: theme, themeTitle: themeTitle, googleAnalyticsKey: appModel.get("googleAnalyticsKey") })); // set up the body this.$el.append(this.template()); // render the nav app.navbarView = new NavbarView(); app.navbarView.setElement($('#Navbar')).render(); app.altHeaderView = new AltHeaderView(); app.altHeaderView.setElement($('#HeaderContainer')).render(); app.footerView = new FooterView(); app.footerView.setElement($('#Footer')).render(); //Load the Slaask chat widget if it is enabled in this theme if(appModel.get("slaaskKey") && window._slaask) _slaask.init(appModel.get("slaaskKey")); // listen for image loading - bind only once in init method var imageEl = $('#bg_image'); if ($(imageEl).length > 0) { // only show the image when it is completely done loading $(imageEl).load(function() { $(imageEl).fadeIn('slow'); }); } //Change the document title when the app changes the appModel title at any time this.listenTo(appModel, "change:title", this.changeTitle); this.listenForActivity(); this.initializeWidgets(); }, //Changes the web document's title changeTitle: function(){ document.title = appModel.get("title"); }, // Render the main view and/or re-render subviews. Don't call .html() here // so we don't lose state, rather use .setElement(). Delegate rendering // and event handling to sub views render: function () { return this; }, // the currently rendered view currentView: null, // Our view switcher for the whole app showView: function(view, viewOptions) { //reference to appView var thisAppViewRef = this; // Change the background image if there is one var imageEl = $('#bg_image'); if ($(imageEl).length > 0) { var imgCnt = $(imageEl).attr('data-image-count'); // hide the existing image $(imageEl).fadeOut('fast', function() { //Randomly choose the next background image var bgNum = Math.ceil(Math.random() * imgCnt); //If the element is an img, change the src attribute if ($(imageEl).prop('tagName') == 'IMG'){ $(imageEl).attr('src', './js/themes/' + theme + '/img/backgrounds/bg' + bgNum + '.jpg'); // note the load() callback will show this image for us } else { //Otherwise, change the background image style property $(imageEl).css('background-image', 'url(\'./js/themes/' + theme + '/img/backgrounds/bg' + bgNum + '.jpg\')'); $(imageEl).fadeIn('slow'); } }); } // close the current view if (this.currentView){ //Check if the view will need to cancel the close if((typeof this.currentView.confirmClose == "function") && (this.currentView != view)){ if(!this.currentView.confirmClose()){ uiRouter.undoLastRoute(); return; } } // need reference to the old/current view for the callback method var oldView = this.currentView; this.currentView.$el.fadeOut('slow', function() { // clean up old view if (oldView.onClose) oldView.onClose(); // render the new view view.$el.hide(); view.render(viewOptions); view.$el.fadeIn('slow', function() { // after fade in, do postRender() if (view.postRender) view.postRender(); // force scroll to top if no custom scrolling is implemented else thisAppViewRef.scrollToTop(); }); }); } else { // just show the view without transition view.render(viewOptions); if (view.postRender) view.postRender(); // force scroll to top if no custom scrolling is implemented else thisAppViewRef.scrollToTop(); } // track the current view this.currentView = view; this.sendAnalytics(); this.trigger("appRenderComplete"); }, sendAnalytics: function(){ if(!appModel.get("googleAnalyticsKey") || (typeof ga === "undefined")) return; var page = window.location.hash || "/"; page = page.replace("#", ""); //remove the leading pound sign ga('send', 'pageview', {'page': page}); }, routeToMetadata: function(e){ e.preventDefault(); //Get the value from the input element var form = $(e.target).attr("form") || null, val = this.$("#" + form).find("input[type=text]").val(); //Remove the text from the input this.$("#" + form).find("input[type=text]").val(""); if(!val) return false; uiRouter.navigate('view/'+ val, {trigger: true}); }, routeToMetadataOnEnter: function(e){ //If the user pressed a key inside a text input, we only want to proceed if it was the Enter key if((e.type == "keypress") && (e.keycode != 13)) return; else this.routeToMetadata(e); }, sendToLogin: function(e){ if(e) e.preventDefault(); var url = $(e.target).attr("href"); url = url.substring(0, url.indexOf("target=")+7); url += window.location.href; window.location.href = url; }, resetSearch: function(){ // Clear the search and map model to start a fresh search appSearchModel.clear(); appSearchModel.set(appSearchModel.defaults); mapModel.clear(); mapModel.set(mapModel.defaults); //Clear the search history appModel.set("searchHistory", new Array()); uiRouter.navigate('data', {trigger: true}); }, closePopovers: function(e){ if(this.currentView && this.currentView.closePopovers) this.currentView.closePopovers(e); }, toggleSlide: function(e){ if(e) e.preventDefault(); else return false; var clickedOn = $(e.target), toggleElId = clickedOn.attr("data-slide-el") || clickedOn.parents("[data-slide-el]").attr("data-slide-el"), toggleEl = $("#" + toggleElId); toggleEl.slideToggle("fast", function(){ //Toggle the display of the link if it has the right class if(clickedOn.is(".toggle-display-on-slide")){ clickedOn.siblings(".toggle-display-on-slide").toggle(); clickedOn.toggle(); } }); }, showAlert: function(msg, classes, container, delay) { if(!classes) var classes = 'alert-success'; if(!container || !$(container).length) var container = this.$el; //Remove any alerts that are already in this container if($(container).children(".alert-container").length > 0) $(container).children(".alert-container").remove(); var alert = $.parseHTML(this.alertTemplate({ msg: msg, classes: classes }).trim()); if(delay){ $(alert).hide(); $(container).prepend(alert); $(alert).show().delay(3000).fadeOut(); } else $(container).prepend(alert); }, // Listens to the focus event on the window to detect when a user switches back to this browser tab from somewhere else // When a user checks back, we want to check for log-in status listenForActivity: function(){ appUserModel.on("change:loggedIn", function(){ if(!appUserModel.get("loggedIn")) return; //Check the user's token on focus $(window).unbind("focus", appUserModel.verifyLoginStatus); $(window).focus(appUserModel.verifyLoginStatus); }); }, openChatWithMessage: function(){ if(!_slaask) return; $("#slaask-input").val(appModel.get("defaultSupportMessage")); $("#slaask-button").trigger("click"); }, initializeWidgets: function(){ // Autocomplete widget extension to provide description tooltips. $.widget( "app.hoverAutocomplete", $.ui.autocomplete, { // Set the content attribute as the "item.desc" value. // This becomes the tooltip content. _renderItem: function( ul, item ) { // if we have a label, use it for the title var title = item.value; if (item.label) { title = item.label; } // if we have a description, use it for the content var content = item.value; if (item.desc) { content = item.desc; if (item.desc != item.value) { content += " (" + item.value + ")"; } } var element = this._super( ul, item ) .attr( "data-title", title ) .attr( "data-content", content ); element.popover( { placement: "right", trigger: "hover", container: 'body' }); return element; } }); }, /********************** Utilities ********************************/ // Various utility functions to use across the app // /************ Function to add commas to large numbers ************/ commaSeparateNumber: function(val){ if(!val) return 0; if(val < 1) return Math.round(val * 100) / 100; while (/(\d+)(\d{3})/.test(val.toString())){ val = val.toString().replace(/(\d+)(\d{3})/, '$1'+','+'$2'); } return val; }, higlightInput: function(e){ if(!e) return; e.preventDefault(); e.target.setSelectionRange(0, 9999); }, widenInput: function(e){ $(e.target).css("width", "200px"); }, narrowInput: function(e){ $(e.target).delay(500).animate({"width": "60px"}); }, // scroll to top of page scrollToTop: function() { $("body,html").stop(true,true) //stop first for it to work in FF .animate({ scrollTop: 0 }, "slow"); return false; }, scrollTo: function(pageElement, offsetTop){ //Find the header height if it is a fixed element var headerOffset = (this.$("#Header").css("position") == "fixed") ? this.$("#Header").outerHeight() : 0; var navOffset = (this.$("#Navbar").css("position") == "fixed") ? this.$("#Navbar").outerHeight() : 0; var totalOffset = headerOffset + navOffset; $("body,html").stop(true,true) //stop first for it to work in FF .animate({ scrollTop: $(pageElement).offset().top - 40 - totalOffset}, 1000); return false; }, //Will pop up an alert asking if the user wants to leave the page or not. confirmLeave: function(e){ var decision = confirm("Do you want to leave this page? All information you've entered will be lost."); return "decision"; } }); return AppView; });