Add baseview(extended from backbone view), which can further extended by other views + Add fucntion to modify iframe and embed tags + add related tests
STUD-823
This commit is contained in:
@@ -199,6 +199,8 @@ define([
|
||||
|
||||
"js/spec/utils/module_spec",
|
||||
"js/spec/models/explicit_url_spec"
|
||||
"js/spec/views/baseview_spec",
|
||||
"js/spec/utils/handle_iframe_binding_spec",
|
||||
|
||||
# these tests are run separate in the cms-squire suite, due to process
|
||||
# isolation issues with Squire.js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require(["domReady", "jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt",
|
||||
"js/utils/get_date", "js/utils/module", "jquery.ui", "jquery.leanModal", "jquery.form", "jquery.smoothScroll"],
|
||||
function(domReady, $, _, gettext, NotificationView, PromptView, DateUtils, ModuleUtils) {
|
||||
"js/utils/get_date", "js/utils/module", "js/utils/handle_iframe_binding", "jquery.ui", "jquery.leanModal", "jquery.form", "jquery.smoothScroll"],
|
||||
function(domReady, $, _, gettext, NotificationView, PromptView, DateUtils, ModuleUtils, IframeUtils) {
|
||||
|
||||
var $body;
|
||||
var $newComponentItem;
|
||||
@@ -113,6 +113,8 @@ domReady(function() {
|
||||
$('.edit-subsection-publish-settings').on('change', '.start-date, .start-time', function() {
|
||||
$('.edit-subsection-publish-settings').find('.save-button').show();
|
||||
});
|
||||
|
||||
IframeUtils.iframeBinding();
|
||||
});
|
||||
|
||||
function smoothScrollLink(e) {
|
||||
|
||||
37
cms/static/js/spec/utils/handle_iframe_binding_spec.js
Normal file
37
cms/static/js/spec/utils/handle_iframe_binding_spec.js
Normal file
@@ -0,0 +1,37 @@
|
||||
define(
|
||||
[
|
||||
"jquery", "underscore",
|
||||
"js/utils/handle_iframe_binding",
|
||||
],
|
||||
function ($, _, IframeBinding) {
|
||||
|
||||
describe("IframeBinding", function () {
|
||||
var doc = document.implementation.createHTMLDocument("New Document");
|
||||
var iframe_html = '<iframe src="http://www.youtube.com/embed/NHd27UvY-lw" frameborder="0" height="350" width="618"></iframe>';
|
||||
iframe_html += '<iframe src="http://www.youtube.com/embed/NHd27UvY-lw?allowFullScreen=false" frameborder="0" height="350" width="618"></iframe>';
|
||||
iframe_html += '<embed type="application/x-shockwave-flash" src="http://www.youtube.com/embed/NHd27UvY-lw" height="315" width="560">';
|
||||
doc.body.innerHTML = iframe_html;
|
||||
|
||||
it("modifies src url of DOM iframe and embed elements when iframeBinding function is executed", function () {
|
||||
expect($(doc).find("iframe")[0].src).toEqual("http://www.youtube.com/embed/NHd27UvY-lw");
|
||||
expect($(doc).find("iframe")[1].src).toEqual("http://www.youtube.com/embed/NHd27UvY-lw?allowFullScreen=false");
|
||||
expect($(doc).find("embed")[0].hasAttribute("wmode")).toBe(false);
|
||||
|
||||
IframeBinding.iframeBinding(doc);
|
||||
|
||||
//after calling iframeBinding function: src url of iframes should have "wmode=transparent" in its querystring
|
||||
//and embed objects should have "wmode='transparent'" as an attribute
|
||||
expect($(doc).find("iframe")[0].src).toEqual("http://www.youtube.com/embed/NHd27UvY-lw?wmode=transparent");
|
||||
expect($(doc).find("iframe")[1].src).toEqual("http://www.youtube.com/embed/NHd27UvY-lw?wmode=transparent&allowFullScreen=false");
|
||||
expect($(doc).find("embed")[0].hasAttribute("wmode")).toBe(true);
|
||||
|
||||
iframe_html = IframeBinding.iframeBindingHtml(iframe_html);
|
||||
|
||||
//after calling iframeBinding function: src url of iframes should have "wmode=transparent" in its querystring
|
||||
//and embed objects should have "wmode='transparent'" as an attribute
|
||||
expect(iframe_html).toEqual('<iframe src="http://www.youtube.com/embed/NHd27UvY-lw?wmode=transparent" frameborder="0" height="350" width="618"></iframe>' +
|
||||
'<iframe src="http://www.youtube.com/embed/NHd27UvY-lw?wmode=transparent&allowFullScreen=false" frameborder="0" height="350" width="618"></iframe>' +
|
||||
'<embed wmode="transparent" type="application/x-shockwave-flash" src="http://www.youtube.com/embed/NHd27UvY-lw" height="315" width="560">');
|
||||
});
|
||||
});
|
||||
});
|
||||
50
cms/static/js/spec/views/baseview_spec.js
Normal file
50
cms/static/js/spec/views/baseview_spec.js
Normal file
@@ -0,0 +1,50 @@
|
||||
define(
|
||||
[
|
||||
"jquery", "underscore",
|
||||
"js/views/baseview",
|
||||
"js/utils/handle_iframe_binding",
|
||||
"sinon"
|
||||
],
|
||||
function ($, _, BaseView, IframeBinding, sinon) {
|
||||
|
||||
describe("BaseView check", function () {
|
||||
var baseView;
|
||||
var iframeBinding_spy;
|
||||
|
||||
beforeEach(function () {
|
||||
iframeBinding_spy = sinon.spy(IframeBinding, "iframeBinding");
|
||||
baseView = BaseView.prototype;
|
||||
|
||||
spyOn(baseView, 'initialize');
|
||||
spyOn(baseView, 'beforeRender');
|
||||
spyOn(baseView, 'render');
|
||||
spyOn(baseView, 'afterRender').andCallThrough();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
iframeBinding_spy.restore();
|
||||
});
|
||||
|
||||
it('calls before and after render functions when render of baseview is called', function () {
|
||||
var baseview_temp = new BaseView()
|
||||
baseview_temp.render();
|
||||
|
||||
expect(baseView.initialize).toHaveBeenCalled();
|
||||
expect(baseView.beforeRender).toHaveBeenCalled();
|
||||
expect(baseView.render).toHaveBeenCalled();
|
||||
expect(baseView.afterRender).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls iframeBinding function when afterRender of baseview is called', function () {
|
||||
var baseview_temp = new BaseView()
|
||||
baseview_temp.render();
|
||||
expect(baseView.afterRender).toHaveBeenCalled();
|
||||
expect(iframeBinding_spy.called).toEqual(true);
|
||||
|
||||
//check calls count of iframeBinding function
|
||||
expect(iframeBinding_spy.callCount).toBe(1);
|
||||
IframeBinding.iframeBinding();
|
||||
expect(iframeBinding_spy.callCount).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
61
cms/static/js/utils/handle_iframe_binding.js
Normal file
61
cms/static/js/utils/handle_iframe_binding.js
Normal file
@@ -0,0 +1,61 @@
|
||||
define(["jquery"], function($) {
|
||||
var iframeBinding = function (e) {
|
||||
var target_element = null;
|
||||
if (typeof(e) == "undefined"){
|
||||
target_element = $("iframe, embed");
|
||||
} else {
|
||||
if (typeof(e.nodeName) != 'undefined'){
|
||||
target_element = $(e).find("iframe, embed");
|
||||
} else{
|
||||
target_element = e.$("iframe, embed");
|
||||
}
|
||||
}
|
||||
modifyTagContent(target_element);
|
||||
};
|
||||
|
||||
var modifyTagContent = function (target_element) {
|
||||
target_element.each(function(){
|
||||
if ($(this).prop('tagName') == 'IFRAME'){
|
||||
var ifr_source = $(this).attr('src');
|
||||
var wmode = "wmode=transparent";
|
||||
if(ifr_source.indexOf('?') != -1) {
|
||||
var getQString = ifr_source.split('?');
|
||||
if (getQString[1].search('wmode=transparent') == -1){
|
||||
var oldString = getQString[1];
|
||||
var newString = getQString[0];
|
||||
$(this).attr('src',newString+'?'+wmode+'&'+oldString);
|
||||
}
|
||||
}
|
||||
else $(this).attr('src',ifr_source+'?'+wmode);
|
||||
}
|
||||
else{
|
||||
$(this).attr('wmode', 'transparent');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Modify iframe/embed tags in provided html string
|
||||
// Use this method when provided data is just html sting not dom element
|
||||
// This method will only modify iframe (add wmode=transparent in url querystring) and embed (add wmode=transparent as attribute)
|
||||
// tags in html string so both tags will attach to dom and don't create z-index problem for other popups
|
||||
// Note: embed tags should be modified before rendering as they are static objects as compared to iframes
|
||||
// Note: this method can modify unintended html (invalid tags) while converting to dom object
|
||||
var iframeBindingHtml = function (html_string) {
|
||||
if (html_string){
|
||||
var target_element = null;
|
||||
var temp_content = document.createElement('div');
|
||||
$(temp_content).html(html_string);
|
||||
target_element = $(temp_content).find("iframe, embed");
|
||||
if (target_element.length > 0){
|
||||
modifyTagContent(target_element);
|
||||
html_string = $(temp_content).html();
|
||||
}
|
||||
}
|
||||
return html_string;
|
||||
};
|
||||
|
||||
return {
|
||||
iframeBinding: iframeBinding,
|
||||
iframeBindingHtml: iframeBindingHtml
|
||||
};
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
define(["backbone", "underscore"], function(Backbone, _) {
|
||||
var AbstractEditor = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore"], function(BaseView, _) {
|
||||
var AbstractEditor = BaseView.extend({
|
||||
|
||||
// Model is MetadataModel
|
||||
initialize : function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "gettext", "js/views/feedback_prompt", "js/views/feedback_notification"],
|
||||
function(Backbone, _, gettext, PromptView, NotificationView) {
|
||||
var AssetView = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "gettext", "js/views/feedback_prompt", "js/views/feedback_notification"],
|
||||
function(BaseView, _, gettext, PromptView, NotificationView) {
|
||||
var AssetView = BaseView.extend({
|
||||
initialize: function() {
|
||||
this.template = _.template($("#asset-tpl").text());
|
||||
this.listenTo(this.model, "change:locked", this.updateLockState);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "js/views/asset"], function(Backbone, AssetView) {
|
||||
define(["js/views/baseview", "js/views/asset"], function(BaseView, AssetView) {
|
||||
|
||||
var AssetsView = Backbone.View.extend({
|
||||
var AssetsView = BaseView.extend({
|
||||
// takes AssetCollection as model
|
||||
|
||||
initialize : function() {
|
||||
|
||||
44
cms/static/js/views/baseview.js
Normal file
44
cms/static/js/views/baseview.js
Normal file
@@ -0,0 +1,44 @@
|
||||
define(
|
||||
[
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
"js/utils/handle_iframe_binding"
|
||||
],
|
||||
function ($, _, Backbone, IframeUtils) {
|
||||
/* This view is extended from backbone with custom functions 'beforeRender' and 'afterRender'. It allows other
|
||||
views, which extend from it to access these custom functions. 'afterRender' function of BaseView calls a utility
|
||||
function 'iframeBinding' which modifies iframe src urls on a page so that they are rendered as part of the DOM.
|
||||
Other common functions which need to be run before/after can also be added here.
|
||||
*/
|
||||
|
||||
var BaseView = Backbone.View.extend({
|
||||
//override the constructor function
|
||||
constructor: function(options) {
|
||||
_.bindAll(this, 'beforeRender', 'render', 'afterRender');
|
||||
var _this = this;
|
||||
this.render = _.wrap(this.render, function (render) {
|
||||
_this.beforeRender();
|
||||
render();
|
||||
_this.afterRender();
|
||||
return _this;
|
||||
});
|
||||
|
||||
//call Backbone's own constructor
|
||||
Backbone.View.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
|
||||
beforeRender: function () {
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return this;
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
IframeUtils.iframeBinding(this);
|
||||
}
|
||||
});
|
||||
|
||||
return BaseView;
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
define(["backbone", "underscore", "jquery"], function(Backbone, _, $) {
|
||||
var ChecklistView = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "jquery"], function(BaseView, _, $) {
|
||||
var ChecklistView = BaseView.extend({
|
||||
// takes CMS.Models.Checklists as model
|
||||
|
||||
events : {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
define(["backbone", "js/views/course_info_update", "js/views/course_info_handout"],
|
||||
function(Backbone, CourseInfoUpdateView, CourseInfoHandoutView) {
|
||||
define(["js/views/baseview", "js/views/course_info_update", "js/views/course_info_handout"],
|
||||
function(BaseView, CourseInfoUpdateView, CourseInfoHandoutView) {
|
||||
/* this view should own everything on the page which has controls effecting its operation
|
||||
generate other views for the individual editors.
|
||||
The render here adds views for each update/handout by delegating to their collections but does not
|
||||
generate any html for the surrounding page.
|
||||
*/
|
||||
|
||||
var CourseInfoEdit = Backbone.View.extend({
|
||||
var CourseInfoEdit = BaseView.extend({
|
||||
// takes CMS.Models.CourseInfo as model
|
||||
tagName: 'div',
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
define(["backbone", "underscore", "codemirror", "js/views/feedback_notification", "js/views/course_info_helper", "js/utils/modal"],
|
||||
function(Backbone, _, CodeMirror, NotificationView, CourseInfoHelper, ModalUtils) {
|
||||
define(["js/views/baseview", "underscore", "codemirror", "js/views/feedback_notification", "js/views/course_info_helper", "js/utils/modal"],
|
||||
function(BaseView, _, CodeMirror, NotificationView, CourseInfoHelper, ModalUtils) {
|
||||
|
||||
// the handouts view is dumb right now; it needs tied to a model and all that jazz
|
||||
var CourseInfoHandoutsView = Backbone.View.extend({
|
||||
var CourseInfoHandoutsView = BaseView.extend({
|
||||
// collection is CourseUpdateCollection
|
||||
events: {
|
||||
"click .save-button" : "onSave",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
define(["codemirror", "utility"],
|
||||
function(CodeMirror) {
|
||||
define(["codemirror", 'js/utils/handle_iframe_binding', "utility"],
|
||||
function(CodeMirror, IframeBinding) {
|
||||
var editWithCodeMirror = function(model, contentName, baseAssetUrl, textArea) {
|
||||
var content = rewriteStaticLinks(model.get(contentName), baseAssetUrl, '/static/');
|
||||
model.set(contentName, content);
|
||||
@@ -18,6 +18,11 @@ define(["codemirror", "utility"],
|
||||
|
||||
var changeContentToPreview = function (model, contentName, baseAssetUrl) {
|
||||
var content = rewriteStaticLinks(model.get(contentName), '/static/', baseAssetUrl);
|
||||
// Modify iframe (add wmode=transparent in url querystring) and embed (add wmode=transparent as attribute)
|
||||
// tags in html string (content) so both tags will attach to dom and don't create z-index problem for other popups
|
||||
// Note: content is modified before assigning to model because embed tags should be modified before rendering
|
||||
// as they are static objects as compared to iframes
|
||||
content = IframeBinding.iframeBindingHtml(content);
|
||||
model.set(contentName, content);
|
||||
return content;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
define(["backbone", "underscore", "codemirror", "js/models/course_update",
|
||||
define(["js/views/baseview", "underscore", "codemirror", "js/models/course_update",
|
||||
"js/views/feedback_prompt", "js/views/feedback_notification", "js/views/course_info_helper", "js/utils/modal"],
|
||||
function(Backbone, _, CodeMirror, CourseUpdateModel, PromptView, NotificationView, CourseInfoHelper, ModalUtils) {
|
||||
function(BaseView, _, CodeMirror, CourseUpdateModel, PromptView, NotificationView, CourseInfoHelper, ModalUtils) {
|
||||
|
||||
var CourseInfoUpdateView = Backbone.View.extend({
|
||||
var CourseInfoUpdateView = BaseView.extend({
|
||||
// collection is CourseUpdateCollection
|
||||
events: {
|
||||
"click .new-update-button" : "onNew",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
define(["backbone", "underscore", "underscore.string", "jquery", "gettext", "js/models/uploads", "js/views/uploads"],
|
||||
function(Backbone, _, str, $, gettext, FileUploadModel, UploadDialogView) {
|
||||
define(["js/views/baseview", "underscore", "underscore.string", "jquery", "gettext", "js/models/uploads", "js/views/uploads"],
|
||||
function(BaseView, _, str, $, gettext, FileUploadModel, UploadDialogView) {
|
||||
_.str = str; // used in template
|
||||
var EditChapter = Backbone.View.extend({
|
||||
var EditChapter = BaseView.extend({
|
||||
initialize: function() {
|
||||
this.template = _.template($("#edit-chapter-tpl").text());
|
||||
this.listenTo(this.model, "change", this.render);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "jquery", "js/views/edit_chapter", "js/views/feedback_notification"],
|
||||
function(Backbone, _, $, EditChapterView, NotificationView) {
|
||||
var EditTextbook = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "jquery", "js/views/edit_chapter", "js/views/feedback_notification"],
|
||||
function(BaseView, _, $, EditChapterView, NotificationView) {
|
||||
var EditTextbook = BaseView.extend({
|
||||
initialize: function() {
|
||||
this.template = _.template($("#edit-textbook-tpl").text());
|
||||
this.listenTo(this.model, "invalid", this.render);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
define(["backbone", "underscore", "underscore.string", "jquery"], function(Backbone, _, str, $) {
|
||||
var SystemFeedback = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "underscore.string", "jquery"], function(BaseView, _, str, $) {
|
||||
var SystemFeedback = BaseView.extend({
|
||||
options: {
|
||||
title: "",
|
||||
message: "",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "jquery", "js/views/edit_textbook", "js/views/show_textbook"],
|
||||
function(Backbone, _, $, EditTextbookView, ShowTextbookView) {
|
||||
var ListTextbooks = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "jquery", "js/views/edit_textbook", "js/views/show_textbook"],
|
||||
function(BaseView, _, $, EditTextbookView, ShowTextbookView) {
|
||||
var ListTextbooks = BaseView.extend({
|
||||
initialize: function() {
|
||||
this.emptyTemplate = _.template($("#no-textbooks-tpl").text());
|
||||
this.listenTo(this.collection, 'all', this.render);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
define(
|
||||
[
|
||||
"backbone", "underscore", "js/models/metadata", "js/views/abstract_editor",
|
||||
"js/views/baseview", "underscore", "js/models/metadata", "js/views/abstract_editor",
|
||||
"js/views/transcripts/metadata_videolist"
|
||||
],
|
||||
function(Backbone, _, MetadataModel, AbstractEditor, VideoList) {
|
||||
function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
|
||||
var Metadata = {};
|
||||
|
||||
Metadata.Editor = Backbone.View.extend({
|
||||
Metadata.Editor = BaseView.extend({
|
||||
|
||||
// Model is CMS.Models.MetadataCollection,
|
||||
initialize : function() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "gettext", "js/models/assignment_grade", "js/views/feedback_notification"],
|
||||
function(Backbone, _, gettext, AssignmentGrade, NotificationView) {
|
||||
var OverviewAssignmentGrader = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "gettext", "js/models/assignment_grade", "js/views/feedback_notification"],
|
||||
function(BaseView, _, gettext, AssignmentGrade, NotificationView) {
|
||||
var OverviewAssignmentGrader = BaseView.extend({
|
||||
// instantiate w/ { graders : CourseGraderCollection, el : <the gradable-status div> }
|
||||
events : {
|
||||
"click .menu-toggle" : "showGradeMenu",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
define(["backbone", "underscore", "js/views/feedback_prompt", "js/views/section_show", "require"], function(Backbone, _, PromptView, SectionShowView, require) {
|
||||
var SectionEdit = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "js/views/feedback_prompt", "js/views/section_show", "require"], function(BaseView, _, PromptView, SectionShowView, require) {
|
||||
var SectionEdit = BaseView.extend({
|
||||
render: function() {
|
||||
var attrs = {
|
||||
name: this.model.escape('name')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "gettext", "js/views/section_edit", "require"], function(Backbone, _, gettext, SectionEditView, require) {
|
||||
define(["js/views/baseview", "underscore", "gettext", "js/views/section_edit", "require"], function(BaseView, _, gettext, SectionEditView, require) {
|
||||
|
||||
var SectionShow = Backbone.View.extend({
|
||||
var SectionShow = BaseView.extend({
|
||||
template: _.template('<span data-tooltip="<%= gettext("Edit this section\'s name") %>" class="section-name-span"><%= name %></span>'),
|
||||
render: function() {
|
||||
var attrs = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt"],
|
||||
function(Backbone, _, gettext, NotificationView, PromptView) {
|
||||
var ShowTextbook = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt"],
|
||||
function(BaseView, _, gettext, NotificationView, PromptView) {
|
||||
var ShowTextbook = BaseView.extend({
|
||||
initialize: function() {
|
||||
this.template = _.template($("#show-textbook-tpl").text());
|
||||
this.listenTo(this.model, "change", this.render);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(["backbone", "underscore", "jquery", "jquery.form"],
|
||||
function(Backbone, _, $) {
|
||||
var UploadDialog = Backbone.View.extend({
|
||||
define(["js/views/baseview", "underscore", "jquery", "jquery.form"],
|
||||
function(BaseView, _, $) {
|
||||
var UploadDialog = BaseView.extend({
|
||||
options: {
|
||||
shown: true,
|
||||
successMessageTimeout: 2000 // 2 seconds
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
define(["backbone", "underscore", "jquery", "gettext", "js/views/feedback_notification", "js/views/feedback_alert", "jquery.smoothScroll"],
|
||||
function(Backbone, _, $, gettext, NotificationView, AlertView) {
|
||||
define(["js/views/baseview", "underscore", "jquery", "gettext", "js/views/feedback_notification", "js/views/feedback_alert", "js/views/baseview", "jquery.smoothScroll"],
|
||||
function(BaseView, _, $, gettext, NotificationView, AlertView) {
|
||||
|
||||
var ValidatingView = Backbone.View.extend({
|
||||
var ValidatingView = BaseView.extend({
|
||||
// Intended as an abstract class which catches validation errors on the model and
|
||||
// decorates the fields. Needs wiring per class, but this initialization shows how
|
||||
// either have your init call this one or copy the contents
|
||||
|
||||
Reference in New Issue
Block a user