define(function(require) { /** * * Extends Backbone.View * @exports views/admin/ClinicDetailsView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Clinic = require('models/ClinicList'), clinicDetailsTpl = require('text!/templates/admin/admin-clinic-details-tpl.html'); ClinicDetailsView = Backbone.View.extend({ initialize: function () { if (this.model != undefined | null) { this.model.on('updated', this.render, this); } }, className: 'clinic-details-el', render: function() { if (this.model == undefined || null) return this; var tpl = _.template(clinicDetailsTpl, {clinic: this.model.toJSON()}); $(this.el).html(tpl); this.delegateEvents(); return this; }, events: { 'click #myonoffswitch': 'onActiveToggle' }, /** * Change the model the instance of ClinicDetailsView is listening to. * @param {Clinic} newModel - The new model the view is to listen to. * An instance of Clinic. */ changeModel: function(newModel) { this.model = newModel; this.render(); this.model.on('updated', this.render, this); }, onActiveToggle: function() { var checked = $(this.el).find('input:checkbox:checked'); var isActive = (checked.length == 0) ? 0 : 1; this.model.set({ isActive: isActive }); this.model.put(); } }); return ClinicDetailsView; });
define(function(require) { /** * Renders and manages events associated with a list element of the list of * categories in the category management window on the administrative page. * Extends Backbone.View * @exports views/admin/CategoryListElementView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Category = require('models/InterestCategory'), categoryListElemTpl = require('text!/templates/admin/category-list-element-tpl.html'); CategoryListElementView = Backbone.View.extend({ initialize: function(options) { /** Reference to parent view. Instance of CategoryListView. */ this.parent = options.parent; this.model.on('destroyed', this.onModelDestroyed, this); }, /** Class name of the <div> DOM element into which this view will render its * template. */ className: 'category-list-elem-el', /** Render view template into this.el */ render: function() { var tpl = _.template(categoryListElemTpl, {category: this.model.toJSON()}); $(this.el).html(tpl); this.delegateEvents(); return this; }, events: { 'click .delete-category-btn': 'onCategoryDelete' }, onCategoryDelete: function() { this.model.destroy(); }, onModelDestroyed: function() { this.parent.trigger('categoryDestroyed', this.model); }, }); return CategoryListElementView; });
define(function(require) { /** * Renders templates and manages events associated with zoomed picture view * modal window displayable on pet profile page. * Extends Backbone.View * @exports views/pet/ZoomPictureView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Picture = require('models/Picture'), zoomPictureTpl = require('text!/templates/pet/zoom-picture-window-tpl.html'); ZoomPictureView = Backbone.View.extend({ initialize: function() { /** The model to which this view listens. Instance of Picture. */ this.model; if (this.model != undefined) this.delegateListeners(); }, /** The class of <div> element appended to the DOM into which this * view renders its template. */ className: 'zoom-picture-el', /** Render view template into this.el */ render: function() { var tpl = _.template(zoomPictureTpl, {picture: this.model.toJSON()}); $(this.el).html(tpl); this.delegateEvents(); return this; }, /** Defines DOM event listeners and callbacks. */ events: { 'click #edit-description-btn': 'renderEditView', 'click #save-description-btn': 'onSaveDescription' }, /** * Called to change the model to which this view listens. * @param {Picture} newModel - the new model to which this view is to listen */ changeModel: function(newModel) { this.unbind(); this.undelegateEvents(); this.$el.off(); this.model = newModel; this.delegateListeners(); this.render(); }, /** Define model event listeners and callbacks. */ delegateListeners: function() { this.model.on('updated', this.renderRegularView, this); }, /** * Replace picture description with editable text box. */ renderEditView: function() { var description = $(this.el).find('.description').html(); var textBoxEl = $('<textarea rows="10" maxlength="120">').val(description); $(this.el).find('#description').html(textBoxEl); $(this.el).find('#edit-description-btn').html('Save Description '); $(this.el).find('#edit-description-btn').append($('<i>').addClass('glyphicon') .addClass('glyphicon-save')); $(this.el).find('#edit-description-btn').attr('id', 'save-description-btn'); this.delegateEvents(); }, /** * Callback to click of save description button. Sets model attributes * and calls Picture.put() */ onSaveDescription: function() { this.model.set({description: $(this.el).find('textarea').val()}); this.model.put(); }, /** * Redisplay the comment associated with the picture in a non-editable * way */ renderRegularView: function() { var description = $(this.el).find('textarea').val(); $(this.el).find('#description').html($('<span>').addClass('description') .html(description)); $(this.el).find('#save-description-btn') .html('Edit Picture Description '); $(this.el).find('#save-description-btn').append($('<i>').addClass('glyphicon') .addClass('glyphicon-edit')); $(this.el).find('#save-description-btn').attr('id', 'edit-description-btn'); this.delegateEvents(); }, /** Display modal window associated with this view. */ show: function() { $(this.el).find('#zoom-picture-window').modal('show'); }, /** Hide modal window associated with this view. */ hide: function() { $(this.el).find('#zoom-picture-window').modal('hide'); } }); return ZoomPictureView; });
define(function(require) { /** * Owner profile view. Manages rendering of elements of owner profile. * Extends Backbone.View * @exports views/owner/OwnerView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Owner = require('models/Owner'), OwnerSidebarView = require('views/owner/OwnerSidebarView'), PetAchievementCompletionView = require('views/owner/PetAchievementCompletionView'), PetListView = require('views/owner/PetListView'), AddPetModalView = require('views/owner/AddPetModalView'); OwnerView = Backbone.View.extend({ initialize: function() { this.isOwnerFetched = false; this.on('addPet', this.onAddPet, this); this.delegateListeners(); }, /** Render View templates into associated DOM element. */ render: function() { // If the owner has not yet been fetched, return. // Note that we are guaranteed that the owner will never be fetched // at the time of instantiation of ownerView. if (!this.isOwnerFetched) return this; this.renderAchievementsView(); this.renderSidebarView(); this.delegateEvents(); return this; }, delegateListeners: function() { this.model.on('fetched', this.onFetch, this); }, // Content rendering functions. -----------------------------------------------/ /** * Instantiate instance of OwnerSidebarView and render it. */ renderSidebarView: function() { if (this.sidebarView == undefined | null) { this.sidebarView = new OwnerSidebarView ( { model: this.model, parent: this } ); } this.sidebarView.render(); $('#profile-sidebar').html(this.sidebarView.el); }, /** * Instantiate instance of PetAchievementCompletionView and render it. */ renderAchievementsView: function() { if (this.achievementsView == undefined | null) { this.achievementsView = new PetAchievementCompletionView( { model: this.model } ); } $('#profile-content').html(this.achievementsView.render().el); }, /** * Callback to 'fetch' event triggered by the instance of Owner model * to which this view listens. */ onFetch: function() { this.isOwnerFetched = true; this.render(); }, /** * Callback to click of "addPet button". Instantiates instance of * AddPetModalView, renders it, and displays the modal window * associated with the view. */ onAddPet: function() { if (this.addPetView == undefined | null) { this.addPetView = new AddPetModalView({ model: Pet.findOrCreate({id: -2, owner: this.model}), owner: this.model }); $('body').append(this.addPetView.render().el); } else { this.addPetView.render(); } this.addPetView.show(); } }); return OwnerView; });
export var ToursView = Backbone.View.extend({ title: _l("Tours"), initialize: function() { var self = this; this.setElement("<div/>"); this.model = new Tours(); this.model.fetch({ success: function() { self.render(); }, error: function() { // Do something. console.error("Failed to fetch tours."); } }); }, render: function() { var tpl = _.template(TOURPAGE_TEMPLATE); var tourtags = {}; _.each(this.model.models, tour => { if (tour.attributes.tags === null) { if (tourtags.Untagged === undefined) { tourtags.Untagged = { name: "Untagged", tours: [] }; } tourtags.Untagged.tours.push(tour); } else { _.each(tour.attributes.tags, tag => { tag = tag.charAt(0).toUpperCase() + tag.slice(1); if (tourtags[tag] === undefined) { tourtags[tag] = { name: tag, tours: [] }; } tourtags[tag].tours.push(tour); }); } }); var tourtagorder = Object.keys(tourtags).sort(); this.$el .html( tpl({ tours: this.model.models, tourtags: tourtags, tourtagorder: tourtagorder }) ) .on("click", ".tourItem", function(e) { e.preventDefault(); giveTourById($(this).data("tour.id")); }) .on("click", ".tag-selector-button", e => { var elem = $(e.target); var display = "block"; var tag = elem.attr("tag-selector-button"); elem.toggleClass("btn-primary"); elem.toggleClass("btn-secondary"); if (elem.hasClass("btn-secondary")) { display = "none"; } $(`div[tag='${tag}']`).css({ display: display }); }); } });
define(function(require) { /** * Renders and manages events related to an element in the list of * achievement sets on the achievements manage page in the administrative * console page. * Extends Backbone.View * @xports views/admin/AchievementSetListElementView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), AchievementSet = require('models/AchievementSetList'), DeleteAchievementSetModalView = require('views/admin/DeleteAchievementSetModalView'), asleViewTpl = require('text!/templates/admin/achievement-set-list-element-tpl.html'); AchievementSetListElementView = Backbone.View.extend({ initialize: function(options) { /** Reference to parent view. Instance of AchievementSetListView. */ this.parent = options.parent; /** Subview handling the deletion of an achievement set. Instance of * DeleteAchievementSetModalView. */ this.deleteAchievementSetView; }, /** Class name of <div> DOM element into which this view renders its * template. */ className: 'asle-el', /** Render view template into this.el */ render: function() { var tpl = _.template(asleViewTpl, {set: this.model.toJSON()}); $(this.el).html(tpl); this.delegateEvents(); return this; }, /** Register DOM event listeners and callbacks. */ events: { 'click': 'onSetClick', 'click .delete-achievement-set-btn': 'onDeleteSet' }, /** Callback to click of list element. Triggers 'setClick' event on parent * view. */ onSetClick: function() { this.parent.trigger('setClick', this); }, /** * Callback to click of achievement set deletion button. * Displays confirmation window. */ onDeleteSet: function() { if (this.deleteAchievementSetView == undefined) { this.deleteAchievementSetView = new DeleteAchievementSetModalView({ model: this.model }); this.deleteAchievementSetView.changeModel(this.model); $('body').append(this.deleteAchievementSetView.render().el); } this.deleteAchievementSetView.show(); }, }); return AchievementSetListElementView; });
var ConfigSettingCollectionView = Backbone.View.extend({ className: "config-settings-view", /** * Renders form for editing configuration settings. */ render: function() { var container = this.$el; this.collection.each((param, index) => { // Hidden params have no representation in the form if (param.get("hidden")) { return; } // Build row for param. var id = `param_${index}`; var type = param.get("type"); var value = param.get("value"); var row = $("<div class='form-row' />").appendTo(container); row.append( $("<label />") .attr("for", id) .text(`${param.get("label")}:`) ); // Draw parameter as checkbox if (type === "bool") { row.append( $('<input type="checkbox" />') .attr("id", id) .attr("name", id) .attr("checked", value) ); } else if (type === "text") { // Draw parameter as textbox row.append( $('<input type="text"/>') .attr("id", id) .val(value) .click(function() { $(this).select(); }) ); } else if (type === "select") { // Draw parameter as select area var select = $("<select />").attr("id", id); _.each(param.get("options"), option => { $("<option/>") .text(option.label) .attr("value", option.value) .appendTo(select); }); select.val(value); row.append(select); } else if (type === "color") { // Draw parameter as color picker var container_div = $("<div/>").appendTo(row); var input = $("<input />") .attr("id", id) .attr("name", id) .val(value) .css("float", "left") .appendTo(container_div) .click(function(e) { // Hide other pickers. $(".tooltip").removeClass("in"); // Show input's color picker. var tip = $(this) .siblings(".tooltip") .addClass("in"); tip.css({ // left: $(this).position().left + ( $(input).width() / 2 ) - 60, // top: $(this).position().top + $(this.height) left: $(this).position().left + $(this).width() + 5, top: $(this).position().top - $(tip).height() / 2 + $(this).height() / 2 }).show(); // Click management: // Keep showing tip if clicking in tip. tip.click(e => { e.stopPropagation(); }); // Hide tip if clicking outside of tip. $(document).bind("click.color-picker", () => { tip.hide(); $(document).unbind("click.color-picker"); }); // No propagation to avoid triggering document click (and tip hiding) above. e.stopPropagation(); }); // Icon for setting a new random color; behavior set below. var new_color_icon = $("<a href='javascript:void(0)'/>") .addClass("icon-button arrow-circle") .appendTo(container_div) .attr("title", "Set new random color") .tooltip(); // Color picker in tool tip style. var tip = $("<div class='tooltip right' style='position: absolute;' />") .appendTo(container_div) .hide(); // Inner div for padding purposes var tip_inner = $("<div class='tooltip-inner' style='text-align: inherit'></div>").appendTo(tip); $("<div class='tooltip-arrow'></div>").appendTo(tip); var farb_obj = $.farbtastic(tip_inner, { width: 100, height: 100, callback: input, color: value }); // Clear floating. container_div.append($("<div/>").css("clear", "both")); // Use function to fix farb_obj value. (fixed_farb_obj => { new_color_icon.click(() => { fixed_farb_obj.setColor(util_mod.get_random_color()); }); })(farb_obj); } else { row.append( $("<input />") .attr("id", id) .attr("name", id) .val(value) ); } // Help text if (param.help) { row.append($("<div class='help'/>").text(param.help)); } }); return this; }, /** * Render view in modal. */ render_in_modal: function(title) { // Set up handlers for cancel, ok button and for handling esc key. var self = this; var cancel_fn = () => { Galaxy.modal.hide(); $(window).unbind("keypress.check_enter_esc"); }; var ok_fn = () => { Galaxy.modal.hide(); $(window).unbind("keypress.check_enter_esc"); self.update_from_form(); }; var check_enter_esc = e => { if ((e.keyCode || e.which) === 27) { // Escape key cancel_fn(); } else if ((e.keyCode || e.which) === 13) { // Enter key ok_fn(); } }; // Set keypress handler. $(window).bind("keypress.check_enter_esc", check_enter_esc); // Show modal. if (this.$el.children().length === 0) { this.render(); } Galaxy.modal.show({ title: title || "Configure", body: this.$el, buttons: { Cancel: cancel_fn, OK: ok_fn } }); }, /** * Update settings with new values entered via form. */ update_from_form: function() { var self = this; this.collection.each((setting, index) => { if (!setting.get("hidden")) { // Set value from view. var id = `param_${index}`; var value = self.$el.find(`#${id}`).val(); if (setting.get("type") === "bool") { value = self.$el.find(`#${id}`).is(":checked"); } setting.set_value(value); } }); } });
define(function (require, exports, module) { var _ = require('libs/underscore'), Backbone = require('libs/backbone'), $ = require('libs/jqueryui'), Busy = require('libs/busy'), Mustache = require('libs/mustache'); //Test = require('app/models/test.js'), //TestList = require('app/models/tests.js'); module.exports = Backbone.View.extend({ events : { "click .submit" : "save", "click .reset" : "reset" }, initialize : function () { //_.bindAll(this,'render'); //this.model.bind('change', this.render); this.render(); }, reset : function () { this.$("form")[0].reset(); return false; }, render : function () { var self = this, url = 'assets/views/addTest.html', content = $("#main-wrap"); //显示加载状态 this._dealLoad(content[0], 'addViewLoad'); //加载view $.get( url, function (data) { $(self.el).html(Mustache.to_html(data, self.model.toJSON() ) ); self.$(".button a").button(); content.html(self.el); module.load('libs/ckeditor/ckeditor',function () { CKEDITOR.replace("tDescribe"); }); self.trigger('addViewLoad'); }); }, /** * * 增加测试,表单提交时触发此函数 */ save : function () { var self = this; this._dealLoad(self.el,'saveTest'); var tDescribe = ''; if(CKEDITOR) { tDescribe = CKEDITOR.instances.tDescribe.getData(); } var data = { tTitle : this.$('[name=tTitle]').val(), tDescribe : tDescribe || this.$('[name=tDescribe]').val() }; var message = this.model.isNew() ? "成功创建测试" : "修改成功"; this.model.save(data, { success : function () { var json = {}; json.message = message; json.buttons = [{ text : '好了,就这样吧', click : function () { $(this).dialog("close"); CKEDITOR && CKEDITOR.instances['tDescribe'].destroy(); self.trigger('testSaved','tests/lists'); } }, { text : '继续修改', click : function () { $(this).dialog("close"); } }]; self._tips('success', json); }, error : function (model,error) { self._tips('error', error ); } }); return false; }, /** * * 控制加载状态提示 * @param node HTMLDOM the node show the loading images * @param event String 加载完成的事件 * @param obj Object 绑定事件的对象 */ _dealLoad : function (node, event, obj) { var loading = Busy(node); obj = obj || this; obj.bind(event, function () { loading.remove(); }); }, _tips : function (type, message) { var self = this; module.load('app/tips', function (Tips) { var tip = new Tips({'position': ['center',100]}); self.trigger("saveTest"); if( type === 'error' ) tip.error(message); else tip.success(message); tip = undefined; }); } }); });
define(function(require) { /** * View used in administrative console. Responsible for managing the rendering of * ManageInterestsView, ManageClinicsView, and ManageAchievementsView. * Extends Backbone.View * @exports views/admin/AdminConsoleView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Admin = require('models/Admin'), adminConsoleTpl = require('text!/templates/admin/admin-console-tpl.html'); AdminConsoleView = Backbone.View.extend({ initialize: function() { }, /** The element this view renders its template into. */ el: 'body', /** Render View template into this.el */ render: function() { var tpl = _.template(adminConsoleTpl, {admin: this.model}); $(this.el).html(tpl); }, /** Defines event listeners and callbacks. */ events: { 'click #manage-clinics-btn': 'showClinics', 'click #manage-interests-btn': 'showInterests', 'click #manage-accounts-btn': 'showAccounts', 'click #manage-achievements-btn': 'showAchievements' }, /** * Highlights the option in the console sidebar given the by the query. * @param {string} query - The query to select the element to which to * add the highlighting. */ highlightOption: function(query) { $(this.el).find('.selection-highlighter').remove(); $(this.el).find('.selected').removeClass('selected'); $(this.el).find(query).addClass('selected'); $(this.el) .find(query) .prepend($('<span>') .addClass('selection-highlighter').html(' ')); }, // Event callback functions ---------------------------------------------------/ /** * Callback on Manage Interest button click. Routes user to appropriate * url such ManageInterestView will be rendered. */ showInterests: function() { this.highlightOption('#manage-interests-btn'); router.navigate('/admin/' + this.model.get('id') + '/interests', true); }, /** * Callback on Manage Clinics button click. Routes user to appropriate * url such that ManageClinicView will be rendered. */ showClinics: function() { this.highlightOption('#manage-clinics-btn'); router.navigate('/admin/' + this.model.get('id') + '/clinics', true); }, /** * Callback on Manage Accounts button click. Routes user to appropriate * url such that ManageAccountsView will be rendered. */ showAccounts: function() { this.highlightOption('#manage-accounts-btn'); router.navigate('/admin/' + this.model.get('id') + '/accounts', true); }, /** Callback on Manage Achievement button click. Routes user to * appropriate url sucht hat the ManageAhievementsView will be rendered. */ showAchievements: function() { this.highlightOption('#manage-achievements-btn'); router.navigate('/admin/' + this.model.get('id') + '/achievements', true); } }); return AdminConsoleView; });
define(function(require) { 'use strict'; var $ = require('libs/jquery'), _ = require('libs/underscore'), Backbone = require('libs/backbone'), dialogTemplate = require('libs/text!./dialog.html'), fieldTemplates = {}; fieldTemplates.checkbox = require('libs/text!./templates/checkbox.html'); fieldTemplates.select = require('libs/text!./templates/select.html'); fieldTemplates.textarea = require('libs/text!./templates/textarea.html'); fieldTemplates.input = require('libs/text!./templates/input.html'); var DialogView = Backbone.View.extend({ template: _.template(dialogTemplate), events: { 'click .execute-btn': 'onExecute', 'click .cancel-btn': 'onCancel', 'click .close': 'close' }, initialize: function() { _.bindAll(this); this.actionsDisabled = false; }, show: function() { this.setElement(this.template(_.extend({ executeButtonText: null, cancelButtonText: null, cssClass: '' }, this.options))); var body = this.$('.modal-body'); (this.options.fields || []).forEach(function(field) { var template = fieldTemplates[field.type]; if(!template) { throw new Error('Field type {type} not recognized.'.replace('{type}', field.type)); } var widget = $(_.template(template)(_.extend({description: '', initialValue: ''}, field))); body.append(widget); if(_.isFunction(field.prePasteHandler) && field.type === 'input') { // TODO: extract this out to widget specific impl. widget.find('input').on('paste', function(e) { var clipboardData = e.originalEvent.clipboardData; if(!clipboardData || !clipboardData.getData) { return; } e.preventDefault(); var text = clipboardData.getData('text/plain').replace(/\r?\n|\r/g, ' '); $(e.target).val(field.prePasteHandler(text)); }); } }); if(this.options.text) { body.append('<p>' + this.options.text + '</p>'); } this.$el.modal({backdrop: 'static'}); this.$el.modal('show'); this.$('textarea, input').first().focus(); }, onExecute: function(e) { e.preventDefault(); var view = this, formData = {}; (this.options.fields || []).forEach(function(field) { var widget = view.$('[name=' + field.name +']'); var widget_type = widget.attr('type'); if (!(widget_type == 'checkbox' || widget_type == 'radio') || widget.is(':checked')) { formData[field.name] = widget.val(); } }); this.trigger('execute', { formData: formData, success: function() { view.actionsDisabled = false; view.close(); }, error: function() { view.actionsDisabled = false; view.close(); }, }); }, onCancel: function() { this.trigger('cancel'); this.close(); }, close: function(e) { if(e) { e.preventDefault(); } if(!this.actionsDisabled) { this.$el.modal('hide'); this.$el.remove(); } this.trigger('close'); }, toggleButtons: function(toggle) { this.$('.btn, button').toggleClass('disabled', !toggle); this.$('textarea').attr('disabled', !toggle); this.actionsDisabled = !toggle; }, setContentView: function(view) { var body = this.$('.modal-body'); body.append(view); } }); return { create: function(config) { return new DialogView(config); } }; });
define(function(require) { /** * Renders and manages events associated * Extends Backbone.View * @exports views/login/LoginView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), formTpl = require('text!/templates/login/form.html'), LoginView = Backbone.View.extend({ /** * Initializes the view by rendering the login page with a login form. * Binds functions in this view to the model. * Bind event listeners. */ initialize:function () { this.render(); // Bind listeners on changes in model. this.model.bind('error', this.onModelError, this); /** Defines DOM element event listeners and callbacks. */ this.events = { 'click #confirm-login-btn': 'login', 'keydown input': 'checkForEnterKey', 'keydown #username': '******', 'keydown #password': '******' }; }, // The container in the DOM for this view to render into. el : $('#login-col'), // Called to render the template to this.el render : function() { var tpl = _.template(formTpl, {}); $(this.el).html(tpl); }, // Getters for values from form inputs // ----------------------------------------/ /** * @return (string) The value input field specified by the given id */ getValById: function(id) { return $(this.el).find('#' + id).val(); }, /** * @return The element with the given class */ getElementClass: function(id) { return $(this.el).find('.' + id); }, // Events listener callbacks --------------------------------------------------/ /** * Set the login data into the model and call the model's save() function. */ login: function (e) { this.model.set({ username: this.getValById('username'), password: this.getValById('password'), role: $(this.el).find('input[name="role"]:checked').val() }); this.model.save(); }, /** * Set the enter key to click submit. */ checkForEnterKey: function(e) { var keyCode = (e.keyCode ? e.keyCode : e.which); if (keyCode == '13') { e.preventDefault(); this.login(); } }, /** * Called when the model this view is observing (an instance of the Login * model) triggers an 'error' event. The expectation is that the callback * will be returned with an object 'errors' which has for each field in the * model and message indicated whether or not validation failed for that * particular attribute. */ onModelError: function(errors) { this.addWarning(this.getElementClass('error-message'), errors.message); }, // Feedback drawing functions -------------------------------------------------/ /** * Remove all feedback from the given element. * @param formGroup The DOM element from which we wish to remove all feedback. */ removeFeedback: function(formGroup) { $(formGroup) .empty(); }, /** * Displays a warning on the given form element. */ addWarning: function(formGroup, message) { // Clear previous messages. this.removeFeedback(formGroup); $(formGroup) .text(message); } }); return LoginView; });
define(function(require) { /** * View responsible for rendering and managing views associated with * managing Interests. * Extends Backbone.View * @exports views/admin/ManageInterestsView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), CategoryList = require('models/CategoryList'), AddInterestView = require('views/admin/AddInterestView'), PreviewInterestView = require('views/admin/PreviewInterestView'), InterestListView = require('views/admin/InterestListView'), ManageCategoriesView = require('views/admin/ManageCategoriesView'), manageInterestsTpl = require('text!/templates/admin/admin-manage-interests-tpl.html'); ManageInterestsView = Backbone.View.extend({ initialize: function() { // Get all categories from the server. /** All categories available at the server. Instance of CategoryList.*/ this.categories = new CategoryList({}); this.listenTo(this.categories, 'changed', this.onCategoriesChanged); this.categories.fetch(); // Listen for click of interest in interest list this.on('interestClick', this.onInterestClick); this.on('newInterest', this.onNewInterest); this.on('interestUpdated', this.onInterestUpdate); }, /** The class name of the div DOM element into which the View will render its template. */ className: 'manage-interests-el', /** Define DOM event listeners and callbacks. */ events: { 'click #show-add-interest-btn': 'showAddInterest', 'click #show-manage-categories-btn': 'showManageCategories' }, showAddInterest: function() { if (this.addInterestView == undefined) { /** Instance of views/admin/AddInterestView. */ this.addInterestView = new AddInterestView( { parent: this, categories: this.categories } ); this.addInterestView.render(); $('html').append(this.addInterestView.el); } this.addInterestView.render().show(); }, showManageCategories: function() { if (this.manageCategoriesView == undefined || null) { /** Instance of views/admin/ManageCategoriesView */ this.manageCategoriesView = new ManageCategoriesView( { parent: this, collection: this.categories, } ); $('html').append(this.manageCategoriesView.render().el); } this.manageCategoriesView.render().show(); }, /** Render View template into this.el. Instanstiates and renders * InterestListView, AddInterestView, and PreviewInterestView. */ render: function() { var tpl = _.template(manageInterestsTpl); $(this.el).html(tpl); // Render subviews. if (this.interestListView == undefined) { /** Instance of views/admin/InterestListView. */ this.interestListView = new InterestListView( { parent: this, categories: this.categories } ); } this.interestListView.render(); $(this.el).find('.interests-list').append(this.interestListView.el); if (this.previewInterestView == undefined) { /** Instance of views/admin/PreviewInterestView. */ this.previewInterestView = new PreviewInterestView( { parent: this, categories: this.categories } ); } this.previewInterestView.render(); $(this.el).find('.preview-interest-panel').append(this.previewInterestView.el); this.delegateEvents(); return this; }, // Event callbacks. -----------------------------------------------------------/ /** * Called by having this.interestListView trigger the 'interest click' event, * such that this class can inform other subviews of the event and let them * handle the event as appropriate. */ onInterestClick: function(e) { this.previewInterestView.changeModel(e.model); this.previewInterestView.remove(); this.previewInterestView.unbind(); this.previewInterestView.undelegateEvents(); this.previewInterestView.$el.off(); this.previewInterestView.render(); $(this.el).find('.preview-interest-panel').append(this.previewInterestView.el); }, /** * Callback on trigger of 'newInterest' by subview. Adds new interest to * InterestListView collection. */ onNewInterest: function(e) { this.interestListView.collection.add(e.interest); }, /** Callback on trigger of 'interestUpdated' event by subview. Re-renders * this.interestListView. */ onInterestUpdate: function(e) { this.interestListView.render(); } }); return ManageInterestsView; });
define(function(require) { /** * Renders and manages events associated with the achievement edit view * view on the achievements management page of the administrative console. * Extends Backbone.View * @exports views/admin/AchievementEditView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), jqueryUi = require('libs/jquery-ui'), Achievement = require('models/Achievement'), PictureUtils = require('utils/PictureUtils'), DeleteAchievementView = require('views/admin/DeleteAchievementModalView'), confirmDeleteTpl = require('text!/templates/admin/achievement-confirm-delete-tpl.html'), achievementEditTpl = require('text!/templates/admin/achievement-edit-tpl.html'); AchievementEditView = Backbone.View.extend({ initialize: function() { /** Instance of PictureUtils. Used in rendering of image associated with * the achievement this view listens to. */ this.picUtils = new PictureUtils({ maxHeight: 80, maxWidth: 80 }); this.model.on('error', this.onModelError, this); this.model.on('updated', this.onModelUpdate, this); /** Boolean value. Changed to true when file associated with this achievement is * changed. */ this.fileChanged = false; /** Subivew handling the deletion of an achievement from the set. * Instance of DeleteAchievementModalView. */ this.deleteAchievementView; }, /** Class name of <div> DOM element into which this view will render its * template. */ className: 'achievement-edit-el', /** Render view template into this.el */ render: function() { $(this.el).addClass('ui-state-default'); var tpl = _.template(achievementEditTpl, {achievement: this.model.toJSON()}); $(this.el).html(tpl); this.picUtils.set({ canvas: $(this.el).find('#resize-canvas').get(0), imgEl: $(this.el).find('img').get(0) }); // Render deletion modal window $('#delete-achievement') this.delegateEvents(); return this; }, /** Define DOM event listeners and callbacks. */ events: { 'change #achievement-img': 'onFileSelect', 'click #save-achievement-btn': 'onAchievementSave', 'click #delete-achievement-btn': 'onAchievementDelete' }, showSpinner: function() { $(this.el).find('.small-spinner').css('opacity', 1); }, hideSpinner: function() { $(this.el).find('.small-spinner').css('opacity', 0); }, /** * Return JSON object of values of inputs in template. * @return {Object} JSON object */ getFormValues: function() { return { name: $(this.el).find('#name').val(), description: $(this.el).find('#description').val(), points: $(this.el).find('#points').val(), imgFile: this.picUtils .dataURItoBlob( $(this.el) .find('#resize-canvas') .get(0) .toDataURL() ) }; }, /** Remove feedback from input elements. */ removeFeedback: function(formGroup) { $(formGroup) .removeClass('has-error') .removeClass('has-success') .find('.feedback').html(''); $(this.el).find('.glyphicon-ok').css('display', 'none'); }, /** * Displays a warning in the feedback div of the given form group element. * @param {domelement} fromGroup - The form group corresponding to the * input with the error. * @param {string} message - The error message to be displayed. */ addWarning: function(formGroup, message) { this.removeFeedback(formGroup); $(formGroup) .addClass('has-error') .find('.feedback').html(message); }, /** * Callback to trigger of "error" event by the model to which this view * listens. Renders errors message to appropriate places in form. */ onModelError: function(err) { this.hideSpinner(); if (err.name != undefined) { this.addWarning( $(this.el).find('#name').closest('.form-group'), err.name ); } if (err.description != undefined) { this.addWarning( $(this.el).find('#description').closest('.form-group'), err.description ); } if (err.points != undefined) { this.addWarning( $(this.el).find('#points').closest('.form-group'), err.points ); } if (err.msg != undefined) { this.addWarning( $(this.el).find('#msg').closest('.form-group'), err.msg ); } }, /** * Listens for the selection of a file. This event handler will be used to * render live preview of the achievement image. */ onFileSelect: function(e) { var imgFile = $(this.el).find('#achievement-img')[0].files[0]; this.fileChanged = true; this.picUtils.loadImg(imgFile); }, /** * Callback to click of save achievement button. Sets model attributes * and call model.post() function. */ onAchievementSave: function() { var self = this; var values = this.getFormValues(); this.showSpinner(); // Remove feedback from input elements. $(this.el).find('.form-group').each(function(index) { self.removeFeedback($(this)); }); if (this.model.validate(values)) { this.model.set(values); this.model.put(this.fileChanged == true ? values.imgFile : null); } }, /** * Callback to click of delete achievement button. Displays modal * confirmation window. */ onAchievementDelete: function(e) { if (this.deleteAchievementView == undefined) { this.deleteAchievementView = new DeleteAchievementView({ model: this.model }); $('body').append(this.deleteAchievementView.render().el); } this.deleteAchievementView.show(); }, /** * Callback to trigger of 'updated' event by model to which this view * listenes. * Removes spinner, provides visual feedback that update was successful. */ onModelUpdate: function(e) { this.hideSpinner(); $(this.el).find('.glyphicon').css('display', 'block'); }, }); return AchievementEditView; });
define(function(require) { /** * View used in pet profile page for displaying the interest list header * and implementing functionality regarding to the addition of an interest to * a pet's profile. * Extends Backbone.View * @exports views/pet/AddInterestView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), InterestRecord = require('models/InterestRecord'), InterestList = require('models/InterestList'), jQueryUI = require('libs/jquery-ui'), addInterestTpl = require('text!/templates/pet/interests-header-tpl.html'); AddInterestView = Backbone.View.extend({ initialize: function(options) { /** * Reference to containing parent view -- an instance of PetSocialView. */ this.parent = options.parent; /** The model this view listens to -- a new instance of InterestRecord */ this.model = InterestRecord.findOrCreate({id: -1}); /** List of all interests currently at server. */ this.interestsList = new InterestList(); /** * Array of descriptions of interests at server -- used to aid in * autocompletion. */ this.values = []; this.delegateListeners(); this.delegateEvents(); this.interestsList.fetch(); }, /** * Class name given to <div> element this view will render its template * into. */ className: 'interest-header-el', /** * Set instance attributes based on object passed. * @param {Object} attributes - JSON objects of instance attributes to be set. */ set: function(attributes) { this.pet = attributes.pet; this.model.set({pet: attributes.pet}); }, /** Render View template into div element. */ render: function() { var tpl = _.template(addInterestTpl); var self = this; $(this.el).html(tpl); if (this.values != null | undefined) { $(this.el).find('#add-interest').autocomplete({ source: self.values }); } this.delegateEvents(); return this; }, /** Defines event listeners and callbacks. */ events: { 'click #add-interest-btn': 'onAddInterest', 'keyup #add-interest': 'onKeyup' }, /** Bind callbacks for object events that the view listens to. */ delegateListeners: function() { this.listenTo(this.interestsList, 'interestsFetched', this.onInterestsFetched); this.listenTo(this.model, 'uploaded', this.onUpload); }, /** * Determines if the given string is a description in the interest list, * returns the interest object whose description matches the string, * @param {string} str - The string value currently in the input field. */ getInterestFromValue: function(str) { var ret = null; this.interestsList.models.forEach(function(interest) { if (interest.get('description') == str) { ret = interest; } }); return ret; }, // Event callbacks ------------------------------------------------------------/ /** * Callback for keyup event -- determines if 'enter' key was pressed and * fires onAddInterest if this is the case. */ onKeyup: function(e) { var keyCode = e.keyCode | e.which; if (keyCode == '13') { this.onAddInterest(); } }, /** * Callback fired on update of list of interests retrieved from server. * Populates this.values array. */ onInterestsFetched: function(e) { this.values = []; var self = this; this.interestsList.models.forEach(function(interest) { self.values.push(interest.get('description')); }); $(this.el).find('#add-interest').autocomplete({ source: self.values }); }, /** * Callback fired on click of add interest button. * Calls the associated InterestRecord's post() method to save interest at * server after validation of value in input. */ onAddInterest: function(e) { var val = $(this.el).find('#add-interest').val(); // Need to confirm that value in input box is in array of allowed interests. var interest = this.getInterestFromValue(val); if (interest == null) return; this.model.set({interest: interest}); this.model.post(); }, /** * Callback fired on trigger of 'uploaded' event by InterestRecord instance * this model is listening to. */ onUpload: function(interestRecord) { this.parent.trigger('interestRecordUpload', interestRecord); this.model = InterestRecord.findOrCreate({id: -1}); this.model.set({pet: interestRecord.get('pet')}); this.render(); this.delegateEvents(); this.delegateListeners(); } }); return AddInterestView; });
define(function(require) { /** * Renders and handles events associate with the owner signup view. * Extends Backbone.View * @exports views/signup/SignupView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), formTpl = require('text!/templates/signup/form.html'), ServerControll = require('controllers/ServerController'); SignupView = Backbone.View.extend({ // Function called on object instantiation. initialize: function() { this.render(); /** The model to which this view listens. Instance of OwnerSignup. */ this.model; // Bind listeners on changes in model. this.model.bind('error', this.onModelError, this); /** Define DOM event listeners and callbacks. */ this.events = { 'click #confirm-signup-btn': 'confirmSignup', 'focusout #email': 'emailChanged', 'focusout #username': '******', 'focusout #password': '******', 'keyup #password-dupl': 'passwordChanged', 'keydown input': 'checkForEnterKey', 'focusin input': 'destroyPopup' }; }, /** The DOM element into which this view's template will be rendered. */ el: $('#signup-col'), // Called to render the template to this.el render: function() { var tpl = _.template(formTpl, {}); $(this.el).html(tpl); }, // Getters for values from form inputs ----------------------------------------/ /** * @return (string) The value input field specified by the given iden. */ getValById: function(id) { return $(this.el).find('#' + id).val(); }, /** * @return The element with class from-group which is closest to the element * with the given id. */ getFormGroupById: function(id) { return $(this.el).find('#' + id).closest('.form-group'); }, // Events listener callbacks --------------------------------------------------/ /** Check for input of enter key -- if is enter key, trigger click of * */ checkForEnterKey: function(e) { var keyCode = (e.keyCode ? e.keyCode : e.which); if (keyCode == '13') { e.preventDefault(); this.confirmSignup(); } }, /** * Callback to click of signup confirmation button. Sets model * attributes and calls associated post() function. */ confirmSignup: function(e) { this.model.set({ email: this.getValById('email'), username: this.getValById('username'), password: this.getValById('password'), passwordDupl: this.getValById('password-dupl') }); this.model.save(); }, /** * Callback to change of email value. Calls model validation functions * to ensure it is an acceptable value. */ emailChanged: function(e) { this.removeFeedback(this.getFormGroupById('email')); this.model.set({email: this.getValById('email')}, {validate: true}); }, /** * Callback to change of username value. Calls model validation functions * to ensure it is an acceptable value. */ usernameChanged: function(e) { this.removeFeedback(this.getFormGroupById('username')); this.model.set({username: this.getValById('username')}, {validate: true}); }, /** * Callback to change of password value. Calls model validation functions * to ensure it is an acceptable value. */ passwordChanged: function(e) { this.removeFeedback(this.getFormGroupById('password')); this.removeFeedback(this.getFormGroupById('password-dupl')); this.model.set({ password: this.getValById('password'), passwordDupl: this.getValById('password-dupl') }, {validate: true}); }, /** * Called when the model this view is observing (an instance of the Owner * model) triggers an 'error' event. The expectation is that the callback * will be returned with an object 'errors' which has for each field in the * model and message indicated whether or not validation failed for that * particular attribute. */ onModelError: function(errors) { if (errors.email != undefined) { this.addWarning(this.getFormGroupById('email'), errors.email); } if (errors.username != undefined) { this.addWarning(this.getFormGroupById('username'), errors.username); } if (errors.password != undefined) { this.addWarning(this.getFormGroupById('password'), errors.password); this.addWarning(this.getFormGroupById('password-dupl')); } }, // Feedback drawing functions -------------------------------------------------/ /** * Destroys the popup over the input field which was called as context for this * callback function. */ destroyPopover: function(e) { $(this).popover('destroy'); }, /** * Remove all feedback from the given form group element. * @param formGroup The DOM element from which we wish to remove all feedback. */ removeFeedback: function(formGroup) { $(formGroup) .removeClass('has-error') .removeClass('has-success'); $(formGroup).find('span') .removeClass('glyphicon-warning-sign') .removeClass('text-danger') .removeClass('text-success') .removeClass('glyphicon-ok') .removeClass('glyphicon-repeat') .removeClass('spin'); $(formGroup).find('input').popover('destroy'); }, /** * Displays a warning on the given form group element and create a popover * with the given message. No popover create if message == "", null or is * undefined. */ addWarning: function(formGroup, message) { // Clear previous messages. this.removeFeedback(formGroup); $(formGroup) .removeClass('has-success') .addClass('has-error'); $(formGroup).find('span') .removeClass('glyphicon-ok') .addClass('glyphicon-warning-sign').removeClass('text-success') .addClass('text-danger'); // If called with no message defined, we should not create a popover. if (message == "" | null | undefined) return; // Else, create new popover with message. $(formGroup).find('input').popover({ placement: 'right', content: function() { // Allows us to redefine popover message content. var msg = message; return msg; } }); $(formGroup).find('input').popover('show'); } }); return SignupView; });
define(function(require) { /** * Resonsible for rendering the window which allows the admin to add a new * new clinic account and handling all associated functionality. * Extends Backbone.View * @exports views/admin/AddClinicView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Clinic = require('models/Clinic'), addClinicModalTpl = require('text!/templates/admin/add-clinic-modal-tpl.html'); AddClinicView = Backbone.View.extend({ initialize: function(options) { /** Reference to parent view. Instance of ManageClinicsView. */ this.parent = options.parent; /** The model to which this view listens. Instance of Clinic.*/ this.model = new Clinic({id: -1}); this.delegateListeners(); }, /** Classname of <div> element to be appended to DOM into which this view * will render its template. */ className: 'add-pet-el', /** Render template to this.el */ render: function() { var tpl = _.template(addClinicModalTpl); $(this.el).html(tpl); this.delegateEvents(); return this; }, /** Show view modal windowl. */ show: function() { $(this.el).find('.modal').modal('show'); }, /** Hide view modal window. */ hide: function() { $(this.el).find('.modal').modal('hide'); }, /** * Return object containing values of input fields in View's template. * @return {Object} A JSON object which as as key values the id's of the * input elements in the view template and values equal to the associated * input values. */ getValuesFromForm: function() { return { name: $(this.el).find('#name').val(), email: $(this.el).find('#email').val(), password: $(this.el).find('#password').val(), repassword: $(this.el).find('#password').val(), address: $(this.el).find('#address').val(), postalCode: $(this.el).find('#postal-code').val(), city: $(this.el).find('#city').val(), province: $(this.el).find('#province').val(), phoneNumber: $(this.el).find('#phone-number').val(), contactPerson: $(this.el).find('#contact-person').val(), website: $(this.el).find('#website').val() } }, /** Register model event listeners and callbacks. */ delegateListeners: function() { this.model.on('coordinatesFetched', this.onCoordinatesFetched, this); this.model.on('coordinatesError', this.onCoordinatesError, this); this.model.on('error', this.onModelError, this); this.model.on('uploaded', this.onUpload, this); }, /** Animate spinner in appropriate element in view template. */ showSpinner: function() { $(this.el).find('.coords-spinner').css('opacity', 1); }, /** Hide animation of spinner in view template. */ hideSpinner: function() { $(this.el).find('.coords-spinner').css('opacity', 0); }, /** Defines DOM event callbacks and listeners. */ events: { 'click .close': 'hide', 'focusout #address': 'getCoordinates', 'focusout #city': 'getCoordinates', 'click #confirm-add-clinic-btn': 'onAddClinic' }, /** * Call model.getCoordinates() function in order to query google * geocoding api to receive latitude and longitude for associated clinic. */ getCoordinates: function() { var attrs = this.getValuesFromForm(); if ((attrs.address == "") || (attrs.city == "")) { return null; } else { this.showSpinner(); this.model.getCoordinates(attrs.address, attrs.city); } }, /** * Callback to succesful return of coordinates by google geocode api call. * @param {Object} rep - The response returned by the google geocode api * call. */ onCoordinatesFetched: function(rep) { this.hideSpinner(); $(this.el).find('.google-feedback') .removeClass('text-danger').addClass('text-success') .html('Google returned coordinates ' + 'for the follow address:<br>' + rep.formatted_address); }, /** * Callback to unsuccessful return of coordinates by the google geocode * api call. * @param {Object} err - The error mesage returned by the Google geocode * api call. */ onCoordinatesError: function(err) { this.hideSpinner(); $(this.el).find('.google-feedback') .removeClass('text-success').addClass('text-danger') .html('Google responded with:\n ' + err); }, /** * Callback to click of confirm-edit-clinic-btn. Sets model attributes * according to form values and calls the associate update function. */ onAddClinic: function() { this.showSpinner(); this.removeFeedback(); var attrs = this.getValuesFromForm(); this.model.set(attrs); this.model.post(); }, /** * Callback to trigger of 'uploaded' event by listened to model associated * with this view. * Triggers 'uploaded' event on parent view and dismisses modal window, * resets this clinic's model. */ onUpload: function() { this.hideSpinner(); this.parent.trigger('uploaded', this.model); this.model = new Clinic({id: -1}); this.hide(); }, onModelError: function(err) { this.hideSpinner(); if (err.email != undefined) { this.addWarning( $(this.el).find('#email').closest('.form-group'), err.email ); } if (err.password != undefined) { this.addWarning( $(this.el).find('#password').closest('.form-group'), err.password ); } }, /** Remove feedback from input elements. */ removeFeedback: function(formGroup) { $(formGroup) .removeClass('has-error') .removeClass('has-success') .find('.feedback').html(''); }, /** * Displays a warning in the feedback div of the given form group element. * @param {domelement} fromGroup - The form group corresponding to the * input with the error. * @param {string} message - The error message to be displayed. */ addWarning: function(formGroup, message) { this.removeFeedback(formGroup); $(formGroup) .addClass('has-error') .find('.feedback').html(message); }, }); return AddClinicView; });
import historyCopyDialog from "mvc/history/copy-dialog"; import ERROR_MODAL from "mvc/ui/error-modal"; import baseMVC from "mvc/base-mvc"; import ajaxQueue from "utils/ajax-queue"; import "ui/search-input"; /* global $ */ /* global Galaxy */ var logNamespace = "history"; /* ============================================================================== TODO: ============================================================================== */ /** @class A container for a history panel that renders controls for that history (delete, copy, etc.) */ var HistoryViewColumn = Backbone.View.extend(baseMVC.LoggableMixin).extend({ _logNamespace: logNamespace, tagName: "div", className: "history-column flex-column flex-row-container", id: function id() { if (!this.model) { return ""; } return `history-column-${this.model.get("id")}`; }, // ------------------------------------------------------------------------ set up /** set up passed-in panel (if any) and listeners */ initialize: function initialize(options) { options = options || {};
define(function(require) { var jquery = require('libs/jquery'), Backbone = require('libs/backbone'), ownerCreateTpl = require('text!/templates/clinic/owner-create-tpl.html'), ownerSuccessTpl = require('text!/templates/clinic/owner-create-success-tpl.html'), searchTpl = require('text!/templates/clinic/owner-search-tpl.html'), profileTpl = require('text!/templates/clinic/owner-profile-tpl.html'); /** * Clinic console view. Used for rendering the clinic console panel along with * the create pet owner account modal window. * * In addition, this view allows the clinic to search for a specific owner by * email or username. * * Lastly, the user may edit the clinic's profile from selecting the My Profile * button at the top right. The user may also logout from the Logout button * at the top right of the page. * * Extends Backbone.View * @exports views/clinic/ClinicView */ ClinicView = Backbone.View.extend({ /** * Initializes this view with the clinic's email address and the * create/search pet owner feature */ initialize: function() { // Insert the left sidebar template this.render(); // Bind listeners on changes in model. this.model.bind('errorNewAccount', this.onCreateModelError, this); this.model.bind('onCreateSuccess', this.onCreateSuccess, this); this.model.bind('onCreateFailure', this.onCreateFailure, this); this.model.bind('error', this.onModelError, this); this.model.bind('onFetched', this.onFetched, this); this.model.bind('onLogout', this.onLogout, this); // Event bindings this.events = { // Create feature 'click #create-owner-btn': 'showCreateOwner', 'click #confirm-create-btn': 'createOwner', 'focusout #owner-username-new': 'usernameChanged', 'focusout #owner-email-new': 'emailChanged', 'keydown #create-owner-form input': 'checkForEnterKeyCreate', 'focusin #create-owner-form input': 'destroyPopup', // Search feature 'click #owner-search-btn': 'searchOwner', 'keydown #owner-email': 'checkForKeyDownSearch', // Logout 'click #logout-btn': 'logout', // Edit profile 'click #myprofile-btn': 'editProfile', }; // Set the base URL for the clinic window.location.base = window.location.pathname; }, // The container in the DOM for this view to render into. el: 'body', // ===================== Getters for form inputs ===========================/ /** * @return (string) The value input field specified by the given id. */ getValById: function(id) { return $(this.el).find('#' + id).val(); }, /** * @return The element with class from-group which is closest to the element * with the given id. */ getFormGroupById: function(id) { return $(this.el).find('#' + id).closest('.form-group'); }, // =================== Content rendering functions =========================/ /** * Render the initial template. */ render: function() { var searchtpl = _.template(searchTpl, {}); $('#search-sidebar').html(searchtpl); var ownercreateTpl = _.template(ownerCreateTpl, {}); $(this.el).append(ownercreateTpl); }, // -------------------------- Create feature -------------------------------/ /** * Set the enter key to click Create Account. */ checkForEnterKeyCreate: function(e) { var keyCode = (e.keyCode ? e.keyCode : e.which); if (keyCode == '13') { e.preventDefault(); this.createOwner(); } }, /** * Show the modal window to create a new pet owner account */ showCreateOwner: function() { // Reset the modal window then show it in ClinicCreatePetView this.removeCreateFeedback($(this.el).find('#owner-email-new').closest('.form-group')); this.removeCreateFeedback($(this.el).find('#owner-username-new').closest('.form-group')); this.destroyPopover(); router.navigate(window.location.base +'/pet', {trigger: true, replace: true}); }, /** * Create the new owner account */ createOwner: function() { this.model.set({ newUsername: this.getValById('owner-username-new'), newEmail: this.getValById('owner-email-new') }); // Validate the owner's fields this.model.validateNewAccount(); // Validate the pet's fields, if it passes, create the owner if (window.router.clinicCreatePetView.addPet()) { this.model.create(); } }, /** * Validate the new username */ usernameChanged: function() { this.removeCreateFeedback(this.getFormGroupById('owner-username-new')); this.model.set({newUsername: this.getValById('owner-username-new')}); this.model.validateNewAccount(); }, /** * Validate the new email */ emailChanged: function() { this.removeCreateFeedback(this.getFormGroupById('owner-email-new')); this.model.set({newEmail: this.getValById('owner-email-new')}); this.model.validateNewAccount(); }, /** * Called when the model this view is observing (an instance of the Owner * model) triggers an 'error' event. The expectation is that the callback * will be returned with an object 'errors' which has for each field in the * model and message indicated whether or not validation failed for that * particular attribute. */ onCreateModelError: function(errors) { if (errors.username != undefined) { this.addCreateWarning(this.getFormGroupById('owner-username-new'), errors.username); } if (errors.email != undefined) { this.addCreateWarning(this.getFormGroupById('owner-email-new'), errors.email); } }, /** * Destroys the popup over the input field which was called as context for this * callback function. */ destroyPopover: function() { $(this).popover('destroy'); }, /** * Remove all feedback from the given form group element. * @param formGroup The DOM element from which we wish to remove all feedback. */ removeCreateFeedback: function(formGroup) { $(formGroup) .removeClass('has-error') .removeClass('has-success'); $(formGroup).find('span') .removeClass('glyphicon-warning-sign') .removeClass('text-danger') .removeClass('text-success') .removeClass('glyphicon-ok') .removeClass('glyphicon-repeat') .removeClass('spin'); $(formGroup).find('input').popover('destroy'); }, /** * Displays a warning on the given form group element and create a popover * with the given message. No popover create if message == "", null or is * undefined. */ addCreateWarning: function(formGroup, message) { // Clear previous messages. this.removeCreateFeedback(formGroup); $(formGroup) .addClass('has-error'); $(formGroup).find('span') .removeClass('glyphicon-ok') .addClass('glyphicon-warning-sign').removeClass('text-success') .addClass('text-danger'); // If called with no message defined, we should not create a popover. if (message == "" | null | undefined) return; // Else, create new popover with message. $(formGroup).find('input').popover({ placement: 'right', content: function() { // Allows us to redefine popover message content. var msg = message; return msg; } }); $(formGroup).find('input').popover('show'); }, /** * This function is called after creating a new owner account has been * successfully completed. It then calls the Pet model to create the new * pet profile of this owner's pet. */ onCreateSuccess: function(message) { // Create pet next window.router.clinicCreatePetView.model.set({ownerId: this.model.get('newEmail')}); window.router.clinicCreatePetView.model.set({owner: this.model.get('newEmail')}); window.router.clinicCreatePetView.model.post(); // Set the router to the base URL router.navigate(window.location.base, {trigger: false, replace: true}); // Insert a success message var ownersuccesstpl = _.template(ownerSuccessTpl, {}); $('#owner-profile').html(ownersuccesstpl); $('#create-owner-success').text(message); $('#create-owner-window').modal('hide'); }, /** * This function is called after creating a new owner account has failed. * It will display the appropriate error messages. */ onCreateFailure: function(message) { // Insert an error message $('#create-owner-form-errors').empty(); $('#create-owner-form-errors').text(message); }, // -------------------------- Search feature -------------------------------/ /** * Set the enter key to click search. */ checkForKeyDownSearch: function(e) { var keyCode = (e.keyCode ? e.keyCode : e.which); if (keyCode == '13') { e.preventDefault(); this.searchOwner(); } }, /** * Search for the specified email with the model's search() function. */ searchOwner: function() { this.model.set({ id: this.$('#owner-email').val() }); this.model.fetch(); }, /** * Called on successfully finding an owner's email. */ onFetched: function() { this.removeFeedback(); this.renderOwner(); }, /** * Render owner's profile panel once a search is successful. */ renderOwner: function() { // Set the router to the correct URL router.navigate(window.location.base +'/owner/' + this.model.get('id'), {trigger: false, replace: true}); // Insert the owner's profile panel var profiletpl = _.template(profileTpl, {}); $('#owner-profile').empty(); $('#owner-profile').html(profiletpl); // Insert the owner's username and email into the panel $('#owner-profile-username').text(this.model.get('username')); $('#owner-profile-email').text(this.model.get('email')); // Populate the owner's pet listing and profile appropriately if (this.model.get('pets').length > 0) { // Redirect to the view that will render the pet listing and profile this.redirectPetList(); } else { $('#pet-panel').html(' This owner has no pets.'); } }, /** * Redirect to the pet list view. */ redirectPetList: function() { // By default, show the first pet router.navigate(window.location.base +'/owner/' + this.model.get('id') + '/pet/' + this.model.get('pets').models[0].get('name'), {trigger: true, replace: true}); }, /** * Called when the model this view is observing (an instance of the Owner * model) triggers an 'error' event. The expectation is that the callback * will be returned with an object 'errors' which has the error message. */ onModelError: function(errors) { // Clear previous messages. this.removeFeedback(); // Add warning this.addWarning(errors.message); }, /** * Remove all feedback from the given element. */ removeFeedback: function() { $('.error-message').empty(); }, /** * Displays a warning on the given form element. */ addWarning: function(message) { $('.error-message').text(message); }, // ------------------------------ Logout -----------------------------------/ /** * Logout from this clinic. */ logout: function() { this.model.logout(); }, /** * Logout successful, redirect to index page. */ onLogout: function() { window.location.replace("/"); }, // --------------------------- Edit Profile --------------------------------/ /** * Edit clinic's profile. */ editProfile: function() { // Route to the editClinicView router.navigate(window.location.base +'/profile', {trigger: true, replace: true}); } }); return ClinicView; });
define(function(require) { /** * Renders and manages views associated with the achievement set addition * modal window on the manage achievement page of the administrative console. * Extends Backbone.View * @exports views/admin/AddAchievementView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), AchievementSet = require('models/AchievementSet'), SpeciesList = require('models/SpeciesList'), addSetTpl = require('text!/templates/admin/add-achievement-set-modal-tpl.html'); AddAchievementSetView = Backbone.View.extend({ initialize: function(options) { /** The model to which this view listens. Instance of AchievementSet. */ this.model; /** Reference to parent view. Instance of AchievementSetListView. */ this.parent = options.parent; /** Collection of species which could potentially be eligible the new * achievement set. Instance of SpeciesList. */ this.species = new SpeciesList(); this.species.on('fetched', this.onSpeciesFetch, this); this.species.fetch(); this.resetModel(); /** Array of species which are available to be declared eligible for the * new achievement. Equal to this.species remove species selected. */ this.speciesAvailable; }, /** * Called on initialization and after successful upload to make this * view listen to a new instance of AchievementSet. */ resetModel: function() { var self = this; this.model = AchievementSet.findOrCreate({id: -1}); this.model.on('uploaded', this.onModelUpload, this); this.model.on('error', this.onModelError, this); this.speciesAvailable = []; this.species.models.forEach(function(s) { self.speciesAvailable.push(s); }); }, /** The class name of <div> DOM element into which this view will render * its template. */ className: 'add-achievement-set-window-el', /** Render viev temlate into this.el */ render: function() { var tpl = _.template(addSetTpl, {}); $(this.el).html(tpl); // Do not render species list if they have not yet been fetched. if (this.species.models.length > 0) { this.renderSpecies(); } this.delegateEvents(); return this; }, /** Populate available list of species ofr this achievement. */ renderSpecies: function() { var self = this; var availableSpeciesList = $(this.el).find('#available-species-list'); var container = document.createDocumentFragment(); container.appendChild(new Option('', '')); this.speciesAvailable.forEach(function(s) { var name = s.get('name'); var id = s.get('id'); container.appendChild( new Option(name, id) ); }); $(availableSpeciesList).html(''); $(availableSpeciesList).append(container); this.delegateEvents(); }, events: { 'click #add-new-set-btn': 'onAddSet', 'click .close': 'hide', 'change #available-species-list': 'onAddSpecies', 'click .remove-species-btn': 'onRemoveSpecies' }, /** * Return array of species objects for each species which has been * selected to be elligible to receive the achievement set. * @return {Array} Array of species JSON objects. */ getSelectedSpecies: function() { var self = this; var ret = []; var list = $(this.el) .find('#selected-species-list') .find('.selected-species'); list.each(function(i, el) { var id = $(el).find('input').val(); ret.push(self.species.getSpeciesById(id).toJSON()); }); return ret; }, /** * Callback to 'fetched' event triggered by the species collection which * this view uses to display which species are eligible for the new * achievement. * Initializes * */ onSpeciesFetch: function() { var self = this; this.speciesAvailable = []; this.species.models.forEach(function(s) { self.speciesAvailable.push(s); }); this.renderSpecies(); }, /** * Return JSON object of values of inputs in the template. * @return {Object} JSON object */ getFormValues: function() { return { name: $(this.el).find('#name').val(), description: $(this.el).find('#description').val(), species: this.getSelectedSpecies() }; }, /** Display modal window associated with this view. */ show: function() { $(this.el).find('.modal').modal('show'); }, /** Hide modal window associated with this view. */ hide: function() { $(this.el).find('.modal').modal('hide'); $('body').removeClass('modal-open'); $('body').find('.modal-backdrop').remove(); }, /** Remove feedback from input elements. */ removeFeedback: function(formGroup) { $(formGroup) .removeClass('has-error') .removeClass('has-success') .find('.feedback').html(''); }, /** * Displays a warning in the feedback div of the given form group element. * @param {domelement} fromGroup - The form group corresponding to the * input with the error. * @param {string} message - The error message to be displayed. */ addWarning: function(formGroup, message) { this.removeFeedback(formGroup); $(formGroup) .addClass('has-error') .find('.feedback').html(message); }, /** * Callback to trigger of "error" event by the model to which this view * listens. Renders errors message to appropriate places in form. */ onModelError: function(err) { if (err.name != undefined) { this.addWarning( $(this.el).find('#name').closest('.form-group'), err.name ); } if (err.description != undefined) { this.addWarning( $(this.el).find('#description').closest('.form-group'), err.description ); } if (err.msg != undefined) { this.addWarning( $(this.el).find('#msg').closest('.form-group'), err.msg ); } }, /** * Callback to 'uploaded' event triggered by the model to which this view * listens. Triggers 'setUploaded' event on parent view, resets the * model to which this view listens, then dismisses the modal window * associated with this view. */ onModelUpload: function() { this.parent.trigger('setUploaded', this.model); this.resetModel(); this.render(); this.delegateEvents(); this.hide(); }, /** * Callback to click of add new achievement set butotn in the add * achievement modal windows. Sets model attributes and calls model.post() */ onAddSet: function(e) { var self = this; var values = this.getFormValues(); // Remove feedback from input elements. $(this.el).find('.form-group').each(function(index) { self.removeFeedback($(this)); }); this.model.set(values); this.model.post(); }, /** * Callback to change event on species select list. Adds species to list * of species ellgible for this particular achievement. */ onAddSpecies: function(e) { var id = e.currentTarget.value; // Select blank option. Do nothing. if (id == "") return; var species = this.species.getSpeciesById(id); var name = species.get('name'); var speciesElem = $('<div>') .addClass('text-info') .addClass('selected-species') .css('cursor', 'pointer'); speciesElem.append( $('<div>') .addClass('pull-right') .addClass('text-danger') .addClass('remove-species-btn') .html('×') ); // Append hidden input field such that id it can retrieved for deletion // and value acquisition operations. speciesElem.append( $('<input type="hidden">').val(id) ); speciesElem.append( $('<div>') .html(name) ); $(this.el).find('#selected-species-list').append(speciesElem); // Remove species selected from those available. this.speciesAvailable = _.filter(this.speciesAvailable, function(s) { return s.get('id') != id; }); this.renderSpecies(); this.delegateEvents(); this.getSelectedSpecies(); }, onRemoveSpecies: function(e) { var selectedSpeciesEl = $(e.currentTarget).closest('.selected-species'); var id = $(selectedSpeciesEl).find('input').val(); // Remove element from DOM. $(selectedSpeciesEl).remove(); // Re-add species to species available array. var species = this.species.getSpeciesById(id); this.speciesAvailable.push(species); this.species.sort(this.speciesAvailable); this.renderSpecies(); this.delegateEvents(); }, }); return AddAchievementSetView; });
define(function(require) { /** * Renders and manages events asociated with the achievement set list displayed * on the achievements management page of the administrative console. * Extends Backbone.View * @exports views/admin/AchievementSetListView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), AchievementSetList = require('models/AchievementSetList'), AddAchievementSetView = require('views/admin/AddAchievementSetView'), AchievementSetListElementView = require('views/admin/AchievementSetListElementView'), achievementSetListTpl = require('text!/templates/admin/achievement-set-list-tpl.html'); AchievementSetListView = Backbone.View.extend({ initialize: function(options) { var self = this; /** Reference to parent view. Instance of ManageAchievementsView. */ this.parent = options.parent; /** Array of subviews. Instances of AchievementSetListElemetnView. */ this.views = []; this.initSubviews(); this.on("setUploaded", this.onSetUpload, this); this.on("setUpdated", this.onSetUpdate, this); }, initSubviews: function() { var self = this; this.views = []; this.collection.models.forEach(function(set) { var aslev = new AchievementSetListElementView ({ model: set, parent: self }); self.views.push(aslev); // Set up views to display by defualt aslev.filters = {}; aslev.filters.descriptionFilter = true; aslev.filters.speciesFilter = true; }); }, destroySubviews: function() { this.views.forEach(function(aselv) { aselv.unbind(); aselv.undelegateEvents(); aselv.$el.off(); aselv.remove(); }); }, /** Class name of <div> DOM element into which this view will renders its * template. */ className: 'achievement-set-list-el', /** Render view template into this.el */ render: function() { var tpl = _.template(achievementSetListTpl, {}); $(this.el).html(tpl); this.renderSubviews(); this.delegateListeners(); this.delegateEvents(); return this; }, /** Render subviews associated with this view and append to appropriate * to appropriate place in DOM */ renderSubviews: function() { $(this.el).find('.list-div').html(''); var container = document.createDocumentFragment(); this.views.forEach(function(aslev) { if (aslev.filters.descriptionFilter == true && aslev.filters.speciesFilter == true) { container.appendChild(aslev.render().el); } }); $(this.el).find('.list-div').append(container); }, /** Define DOM event listeners and callbacks. */ events: { 'keyup #description-filter': 'filterByDescriptionAndName', 'keyup #species-filter': 'filterBySpecies', 'click #add-achievement-set-btn': 'onAddSet', }, /** * Build and cache regexp used in filtering. Cache hash is the regular * expression built from the passed pattern. * @param {string} pattern - The string from which to build a matching * pattern. * @returns {regexp} A regular expression */ cache: _.memoize(function(pattern) { return new RegExp(pattern.split('').reduce(function(a, b) { return a+'[^'+b+']*'+b; })) }), /** * Perform test for fuzzy match of str against pattern. * @param {string} str - The string to be matched. * @param {string} pattern - The pattern against which to match str. * @return {boolean} true if str is fuzzy match of pattern, false otherwise. */ match: function(pattern, str) { if (pattern == "") return; return this.cache(pattern).test(str); }, /** Filter which achievement sets are displayed in the list based on the * value in the species search box. */ filterBySpecies: function(e) { var self = this; var query = (e != undefined) ? e.currentTarget.value.toLowerCase() : $(this.el).find('#species-filter').val(); if (query == "") { // If query is the empty string, do not filter by species this.views.forEach(function(aslev) { aslev.filters.speciesFilter = true; }); } else { this.views.forEach(function(aslev) { // Break list of species into array, separate query at ' 's // Try matching every species element against every query element var queries = query.split(' '); queries = _.filter(queries, function(q) {return q != ""}); var terms = $(aslev.el).find('.eligible-species-list').html().toLowerCase(); var toMatch = terms.split(','); var querySz = queries.length; // Default to false var matches = 0; aslev.filters.speciesFilter = false; toMatch.forEach(function(term) { queries.forEach(function(q) { if (self.match(q.toLowerCase(), term.toLowerCase())) { matches++; } }); }); if (matches >= querySz) aslev.filters.speciesFilter = true; }); } this.renderSubviews(); }, /** Filter which achievement sets are displayed in the list based on the * value in the name/description search box. */ filterByDescriptionAndName: function(e) { var self = this; var query = (e != undefined) ? e.currentTarget.value.toLowerCase() : $(this.el).find('#description-filter').val(); if (query == "") { // If query is the empty string, do not filter by name or description this.views.forEach(function(aslev) { aslev.filters.descriptionFilter = true; }); } else { this.views.forEach(function(aslev) { var toMatch = aslev.model.get('description').toLowerCase(); toMatch += ' ' + aslev.model.get('name').toLowerCase(); if (self.match(query, toMatch)) { aslev.filters.descriptionFilter = true; } else { aslev.filters.descriptionFilter = false; } }); } this.renderSubviews(); }, delegateListeners: function() { this.on('setClick', this.onSetClick, this); }, /** * Called when subview triggers 'setCLick' event. * Highlights the list element that was click and passes model corresponding * to element clicked to parent view. */ onSetClick: function(e) { // Remove clicked attribute from list elements and add clicked class // to the event target $(this.el).find('.achievement-set-list-element').removeClass('clicked'); $(e.el).find('.achievement-set-list-element').addClass('clicked'); // Trigger event on parent view. this.parent.trigger('setClick', e); }, /** * Callback to click of Add Achievement Set button. Displays achievement * set addition modal window. */ onAddSet: function() { if (this.addAchievementSetView == undefined) { this.addAchievementSetView = new AddAchievementSetView({ parent: this }); $('body').append(this.addAchievementSetView.render().el); } this.addAchievementSetView.show(); }, /** Callback to trigger of "setUploaded" event by AddAchievement view. * Adds a new subview to render the new set. */ onSetUpload: function(e) { var self = this; if (_.find(this.views, function(v) {return e.get('id') == v.model.get('id')}) == undefined) { var aslev = new AchievementSetListElementView ({ model: e, parent: self }); self.views.push(aslev); // Set up new view to display by defualt aslev.filters = {}; aslev.filters.descriptionFilter = true; aslev.filters.speciesFilter = true; this.renderSubviews(); } }, /** Callback to trigger of "setUpdated" event by parent view. Re-renders * the list element corresponding to the updated achievement set. */ onSetUpdate: function(e) { var self = this; this.views.forEach(function(aslev) { if (aslev.model.get('id') == e.get('id')) { aslev.render(); aslev.delegateEvents(); } }); }, }); return AchievementSetListView; });
define(function(require) { /** * View which renders and manages events associated picture carousel on * pet profile page. * Extends Backbone.View * @exports views/pet/CarouselPetView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), CarouselPictureView = require('views/pet/CarouselPictureView') carouselTpl = require('text!/templates/pet/profile-pic-carousel-tpl.html'); CarouselView = Backbone.View.extend({ initialize: function(options) { /** Reference to parent view. Instance of PetSocialInfoView. */ this.parent = options.parent; var self = this; /** Collection of subviews. Instances of CarouselPictureView. */ this.views = []; if (this.collection != undefined) { this.collection.forEach(function(picture) { var cpv = new CarouselPictureView({model: picture, parent: self}); self.views.push(cpv) }); } this.on('zoom', this.onZoom, this); this.delegateListeners(); }, /** DOM element into which this view will render its template. */ className: 'carousel-el', /** * Render View template into this.el * For each Picture in the collection that this instance of CarouselView * listens to, we instantiate an instance of CarouselPictureView and * render it into appropriate <div> in the template. */ render: function() { var tpl = _.template(carouselTpl); var isActive = 1; var self = this; $(this.el).html(tpl); // Render subviews. this.views.forEach(function(cpv) { if (isActive == 1 && self.didThis == 0) { $(self.el) .find('.carousel-inner') .append($(cpv.render().el).addClass('active')); } else { $(self.el) .find('.carousel-inner') .append(cpv.render().el); } isActive = 0; }); return this; }, /** * Set instance attributes based on object passed. * @param {object} attributes JSON Object with instance attributes to be set. */ set: function(attributes) { var self = this; this.didThis = 0; if (attributes.collection != undefined | null) { this.collection = attributes.collection; this.views = []; if (this.collection != undefined) { this.collection.forEach(function(picture) { var cpv = new CarouselPictureView({model: picture, parent: self}); self.views.push(cpv) }); } } this.delegateListeners(); }, /** Bind callbacks for object events that the view listens to. */ delegateListeners: function() { if (this.collection == undefined | null) return this; this.collection.bind('remove', this.onModelDestroyed, this); this.collection.bind('add', this.addOne, this); }, /** Defines event listeners and callbacks. */ events: { }, // Event callback functions. --------------------------------------------------/ /** * Called on addition of new Picture to collection. Renders new picture * into carousel. * @param {Picture} newPicture - The instance of Picture model which was added * to the collection this View listens to. */ addOne: function(newPicture) { var self = this; if (this.views.length == 0) { this.didThis = 1; var cpv = new CarouselPictureView({model: newPicture, parent: this}); this.views.push(cpv); $(this.el) .find('.carousel-inner') .append($(cpv.render().el).addClass('active')); } else { var result = _.filter(this.views, function(view) { return view.model.get('id') == newPicture.get('id'); }); if (result.length == 0) { // If length == 0, safe to instantiate new view. var cpv = new CarouselPictureView({model: newPicture, parent: this}); this.views.push(cpv); $(this.el).find('.carousel-inner').append(cpv.render().el); } else { // Do not append view to DOM, do not instantiate } } return this; }, /** Callback to trigger of 'zoom' event by subview. Trigger same event * on parent view and pass along same context. */ onZoom: function(picture) { this.parent.trigger('zoom', picture); }, /** Callback to destruction of picture model in collection. Re-renders the View. */ onModelDestroyed: function(pic) { this.render(); } }); return CarouselView; });
define(function(require) { /** * Renders and manages events associated with the accounts management view * on the administrative console page. * Extends Backbone.View * @exports views/admin/ManageCategoriesView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Owner = require('models/Owner'), PetListView = require('views/admin/PetListView'), manageAccountsTpl = require('text!/templates/admin/manage-accounts-tpl.html'); ManageAccountsView = Backbone.View.extend({ initialize: function() { this.model = Owner.findOrCreate({id: -1}); this.petListView = new PetListView(); this.model.on('fetched', this.onOwnerFetch, this); this.model.on('error', this.onOwnerError, this); }, /** Class name of <div> DOM element into which this view will render its * template. */ className: 'manage-accounts-el', /** Render view template into this.el */ render: function() { var tpl = _.template(manageAccountsTpl, {owner: this.model.toJSON()}); $(this.el).html(tpl); this.delegateEvents(); this.hideSpinner(); return this; }, /** Render list of pets. */ renderPetList: function() { if (this.petListView == undefined) { // this.petListView = new PetListView({}); } }, /** Defines DOM element listeners and callbacks. */ events: { 'keyup #owner-search-input': 'onKeyUp', 'click #myonoffswitch': 'onActiveToggle' }, /** Animate spinner in appropriate element in view template. */ showSpinner: function() { $(this.el).find('.coords-spinner') .css('height', '68px') .css('width', '68px') .css('opacity', 1); }, /** Hide animation of spinner in view template. */ hideSpinner: function() { $(this.el).find('.coords-spinner') .css('opacity', 0) .css('height', '0px') .css('width', '0px'); }, // Event callbacks. -----------------------------------------------------------/ /** * Callback to toggle of inactive/active switch displayed with other * owner information. */ onActiveToggle: function(e) { var lastActiveVal = parseInt($(this.el).find('input:checkbox').val()); this.model.set({isActive: (lastActiveVal + 1) % 2}); if (lastActiveVal == 0) { $(this.el).find('input:checkbox').val("1"); this.model.put(); } else { $(this.el).find('input:checkbox').val("0"); this.model.destroy(); } }, /** * Callback to keyup event in owner search input. * If enter key is pressed, initiates search for owner. */ onKeyUp: function(e) { var keyCode = e.keyCode || e.which; if (keyCode == '13') { this.onOwnerSearch(); } }, /** * Callback to click of search for owner button. Sets * this.owner's id and calls its fetch() method. */ onOwnerSearch: function() { var id = $(this.el).find('#owner-search-input').val(); $(this.el).find('.feedback').html(''); this.model.set({id: id}); this.model.fetch(); this.showSpinner(); }, /** * Callback to trigger of 'fetched' event by the model * to which this view listens. Updates petlistview */ onOwnerFetch: function() { this.hideSpinner(); this.render(); this.petListView.changeModel(this.model); $(this.el).find('.pet-list-panel').html(this.petListView.render().el); }, /** * Callback to trigger of "error" by the model to which this * view listens. Renders error message to template. */ onOwnerError: function(err) { this.hideSpinner(); $(this.el).find('.feedback').html(err.message); } }); return ManageAccountsView; });
define(function(require) { /** * View which renders and manages events associated with the picture management * window on the pet profile page. * Extends Backbone.View * @exports views/pet/ManagePicturesView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Pictures = require('models/Admin'), GridPictureView = require('views/pet/GridPictureView'), UploadPictureModalView = require('views/pet/UploadPictureModalView'), mangagePicturesTpl = require('text!/templates/pet/manage-pictures-window-tpl.html'); ManagePicturesView = Backbone.View.extend({ initialize: function(options) { /** Reference to parent view. Instance of views/pet/PetSocialInfoView. */ this.parent = options.parent; /** Instance of Pet model for which the pictures in this view belong. */ this.pet = options.pet; /** The collection to which this View listens. Instance of PictureList. */ this.collection = options.collection; this.registerListeners(); }, /** * Class name given to <div> element this view will render its template * into. */ className: 'manage-pictures-el', /** Render View template into div element. */ render: function() { var tpl = _.template(mangagePicturesTpl); var self = this; $(this.el).html(tpl); this.collection.forEach(function(picture) { var gridPicture = new GridPictureView({parent: self, model: picture}); $(self.el).find('.picture-grid').find('.row').append(gridPicture.render().el); }); this.delegateEvents(); return this; }, /** Show modal window associated with this View. */ show: function() { $(this.el).find('#manage-pictures-window').modal('show'); }, /** hide modal window associated with this View. */ hide: function() { $(this.el).find('#manage-pictures-window').modal('hide'); }, /** Defines event listeners and callbacks. */ events: { 'click #upload-picture-btn': 'showUploadPicture', 'click img': 'showZoomPicture' }, /** Bind callbacks for object events that the view listens to. */ registerListeners: function() { this.collection.bind('add', this.addOne, this); this.on('zoom', this.onZoom, this); this.on('modelDestroyed', this.onModelDestroyed, this); }, // Event callback functions ---------------------------------------------------/ /** * Called on addition of new Picture to collection. Renders new picture * into carousel. * @param {Picture} newPicture - The instance of Picture model which was added * to the collection this View listens to. */ addOne: function(newPicture) { var gridPicture = new GridPictureView({parent: this, model: newPicture}); $(this.el) .find('.picture-grid') .find('.row') .append(gridPicture.render().el); }, onModelChange: function() { }, /** * Instantiate UploadPictureView and display modal window. */ showUploadPicture: function() { if (this.uploadPictureWindow == undefined | null) { this.uploadPictureWindow = new UploadPictureModalView( { pictures: this.collection } ); $('html').append(this.uploadPictureWindow.render().el); this.uploadPictureWindow.show(); } else { this.uploadPictureWindow.render(); this.uploadPictureWindow.show(); } }, /** * Subivew triggered 'zoom' event, pass it up the chain to PetSocialInfoView * to handle actually rendering and displaying zoomed picture view. */ onZoom: function(picture) { this.parent.trigger('zoom', picture); }, /** * A model was deleted. Remove model from collection, destroy it, * and destroy subview. */ onModelDestroyed: function(picture, view) { this.collection.remove(picture); view.remove(); } }); return ManagePicturesView; });
define(function(require) { /** * View rendering view of picture in picture grid in picture management window * on pet profile page. Also handles associated events. * Extends Backbone.View * @exports views/pet/GridPictureView */ var jquery = require('libs/jquery'), underscore = require('libs/underscore'), Backbone = require('libs/backbone'), Picture = require('models/Picture'), gridPictureTpl = require('text!/templates/pet/grid-picture-tpl.html'); GridPictureView = Backbone.View.extend({ initialize: function(options) { /** Reference to parent view. Instance of views/pet/ManagePicturesView. */ this.parent = options.parent; /** The model this View listens to. Instance of Picture. */ this.model = options.model; this.delegateListeners(); }, /** Render View template into div element. */ render: function() { $(this.el) .addClass('col-xs-4') .addClass('padded-top') .addClass('picture-col'); var tpl = _.template(gridPictureTpl, {picture: this.model.toJSON()}); $(this.el).html(tpl); this.delegateEvents(); return this; }, /** Defines event listeners and callbacks. */ events: { 'mouseover': 'onMouseover', 'mouseleave': 'onMouseleave', 'click img': 'onImgClick', 'click .picture-delete': 'onPictureDelete' }, /** Register model event listeners and callbacks. */ delegateListeners: function() { this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'destroy', this.onModelDestroy); }, /** * Change the model to which this view listens. * @param {Picture} newModel - The new model to which this view is to listen. */ changeModel: function(newModel) { /** The model to which this view listens. Instance of Picture. */ this.model = newModel; this.delegateEvents(); this.delegateListeners(); }, // Event callback functions. --------------------------------------------------/ /** Callback to event of 'mouseover' on a picture . Sets opacity * of picture stats to 1. */ onMouseover: function(e) { $(this.el).find('.picture-stats').css('opacity', 1); $(this.el).find('.picture-delete').css('opacity', 1); }, /** Callback to event of 'mouseleave' on a picture. Sets opacity * of picture stats to 1. */ onMouseleave: function(e) { $(this.el).find('.picture-stats').css('opacity', 0); $(this.el).find('.picture-delete').css('opacity', 0); }, /** Callback to click of image in grid. Triggers 'zoom' event on parent view. */ onImgClick: function(e) { this.parent.trigger('zoom', this.model); }, /** Callback to click of picture deletion button in grid. Destroy model*/ onPictureDelete: function(e) { this.model.destroy(); }, /** Callback to 'destroy' event triggered by model this View listens to. Gracefully animates this picture away from the grid and triggers 'modelDestroyed' event on parent view.*/ onModelDestroy: function(model) { var self = this; $(this.el).css('opacity', 0); setInterval(function() { self.parent.trigger('modelDestroyed', model, self); }, 1000); } }); return GridPictureView; });