Commit a6fbcbd7 by Zubair Afzal

Add baseview(extended from backbone view), which can further extended by other…

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
parent 608aaf31
...@@ -199,6 +199,8 @@ define([ ...@@ -199,6 +199,8 @@ define([
"js/spec/utils/module_spec", "js/spec/utils/module_spec",
"js/spec/models/explicit_url_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 # these tests are run separate in the cms-squire suite, due to process
# isolation issues with Squire.js # isolation issues with Squire.js
......
require(["domReady", "jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt", 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"], "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) { function(domReady, $, _, gettext, NotificationView, PromptView, DateUtils, ModuleUtils, IframeUtils) {
var $body; var $body;
var $newComponentItem; var $newComponentItem;
...@@ -113,6 +113,8 @@ domReady(function() { ...@@ -113,6 +113,8 @@ domReady(function() {
$('.edit-subsection-publish-settings').on('change', '.start-date, .start-time', function() { $('.edit-subsection-publish-settings').on('change', '.start-date, .start-time', function() {
$('.edit-subsection-publish-settings').find('.save-button').show(); $('.edit-subsection-publish-settings').find('.save-button').show();
}); });
IframeUtils.iframeBinding();
}); });
function smoothScrollLink(e) { function smoothScrollLink(e) {
......
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&amp;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">');
});
});
});
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);
});
});
});
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
};
});
\ No newline at end of file
define(["backbone", "underscore"], function(Backbone, _) { define(["js/views/baseview", "underscore"], function(BaseView, _) {
var AbstractEditor = Backbone.View.extend({ var AbstractEditor = BaseView.extend({
// Model is MetadataModel // Model is MetadataModel
initialize : function() { initialize : function() {
......
define(["backbone", "underscore", "gettext", "js/views/feedback_prompt", "js/views/feedback_notification"], define(["js/views/baseview", "underscore", "gettext", "js/views/feedback_prompt", "js/views/feedback_notification"],
function(Backbone, _, gettext, PromptView, NotificationView) { function(BaseView, _, gettext, PromptView, NotificationView) {
var AssetView = Backbone.View.extend({ var AssetView = BaseView.extend({
initialize: function() { initialize: function() {
this.template = _.template($("#asset-tpl").text()); this.template = _.template($("#asset-tpl").text());
this.listenTo(this.model, "change:locked", this.updateLockState); this.listenTo(this.model, "change:locked", this.updateLockState);
......
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 // takes AssetCollection as model
initialize : function() { initialize : function() {
......
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;
});
\ No newline at end of file
define(["backbone", "underscore", "jquery"], function(Backbone, _, $) { define(["js/views/baseview", "underscore", "jquery"], function(BaseView, _, $) {
var ChecklistView = Backbone.View.extend({ var ChecklistView = BaseView.extend({
// takes CMS.Models.Checklists as model // takes CMS.Models.Checklists as model
events : { events : {
......
define(["backbone", "js/views/course_info_update", "js/views/course_info_handout"], define(["js/views/baseview", "js/views/course_info_update", "js/views/course_info_handout"],
function(Backbone, CourseInfoUpdateView, CourseInfoHandoutView) { function(BaseView, CourseInfoUpdateView, CourseInfoHandoutView) {
/* this view should own everything on the page which has controls effecting its operation /* this view should own everything on the page which has controls effecting its operation
generate other views for the individual editors. generate other views for the individual editors.
The render here adds views for each update/handout by delegating to their collections but does not The render here adds views for each update/handout by delegating to their collections but does not
generate any html for the surrounding page. generate any html for the surrounding page.
*/ */
var CourseInfoEdit = Backbone.View.extend({ var CourseInfoEdit = BaseView.extend({
// takes CMS.Models.CourseInfo as model // takes CMS.Models.CourseInfo as model
tagName: 'div', tagName: 'div',
......
define(["backbone", "underscore", "codemirror", "js/views/feedback_notification", "js/views/course_info_helper", "js/utils/modal"], define(["js/views/baseview", "underscore", "codemirror", "js/views/feedback_notification", "js/views/course_info_helper", "js/utils/modal"],
function(Backbone, _, CodeMirror, NotificationView, CourseInfoHelper, ModalUtils) { function(BaseView, _, CodeMirror, NotificationView, CourseInfoHelper, ModalUtils) {
// the handouts view is dumb right now; it needs tied to a model and all that jazz // 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 // collection is CourseUpdateCollection
events: { events: {
"click .save-button" : "onSave", "click .save-button" : "onSave",
......
define(["codemirror", "utility"], define(["codemirror", 'js/utils/handle_iframe_binding', "utility"],
function(CodeMirror) { function(CodeMirror, IframeBinding) {
var editWithCodeMirror = function(model, contentName, baseAssetUrl, textArea) { var editWithCodeMirror = function(model, contentName, baseAssetUrl, textArea) {
var content = rewriteStaticLinks(model.get(contentName), baseAssetUrl, '/static/'); var content = rewriteStaticLinks(model.get(contentName), baseAssetUrl, '/static/');
model.set(contentName, content); model.set(contentName, content);
...@@ -18,6 +18,11 @@ define(["codemirror", "utility"], ...@@ -18,6 +18,11 @@ define(["codemirror", "utility"],
var changeContentToPreview = function (model, contentName, baseAssetUrl) { var changeContentToPreview = function (model, contentName, baseAssetUrl) {
var content = rewriteStaticLinks(model.get(contentName), '/static/', 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); model.set(contentName, content);
return content; return content;
}; };
......
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"], "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 // collection is CourseUpdateCollection
events: { events: {
"click .new-update-button" : "onNew", "click .new-update-button" : "onNew",
......
define(["backbone", "underscore", "underscore.string", "jquery", "gettext", "js/models/uploads", "js/views/uploads"], define(["js/views/baseview", "underscore", "underscore.string", "jquery", "gettext", "js/models/uploads", "js/views/uploads"],
function(Backbone, _, str, $, gettext, FileUploadModel, UploadDialogView) { function(BaseView, _, str, $, gettext, FileUploadModel, UploadDialogView) {
_.str = str; // used in template _.str = str; // used in template
var EditChapter = Backbone.View.extend({ var EditChapter = BaseView.extend({
initialize: function() { initialize: function() {
this.template = _.template($("#edit-chapter-tpl").text()); this.template = _.template($("#edit-chapter-tpl").text());
this.listenTo(this.model, "change", this.render); this.listenTo(this.model, "change", this.render);
......
define(["backbone", "underscore", "jquery", "js/views/edit_chapter", "js/views/feedback_notification"], define(["js/views/baseview", "underscore", "jquery", "js/views/edit_chapter", "js/views/feedback_notification"],
function(Backbone, _, $, EditChapterView, NotificationView) { function(BaseView, _, $, EditChapterView, NotificationView) {
var EditTextbook = Backbone.View.extend({ var EditTextbook = BaseView.extend({
initialize: function() { initialize: function() {
this.template = _.template($("#edit-textbook-tpl").text()); this.template = _.template($("#edit-textbook-tpl").text());
this.listenTo(this.model, "invalid", this.render); this.listenTo(this.model, "invalid", this.render);
......
define(["backbone", "underscore", "underscore.string", "jquery"], function(Backbone, _, str, $) { define(["js/views/baseview", "underscore", "underscore.string", "jquery"], function(BaseView, _, str, $) {
var SystemFeedback = Backbone.View.extend({ var SystemFeedback = BaseView.extend({
options: { options: {
title: "", title: "",
message: "", message: "",
......
define(["backbone", "underscore", "jquery", "js/views/edit_textbook", "js/views/show_textbook"], define(["js/views/baseview", "underscore", "jquery", "js/views/edit_textbook", "js/views/show_textbook"],
function(Backbone, _, $, EditTextbookView, ShowTextbookView) { function(BaseView, _, $, EditTextbookView, ShowTextbookView) {
var ListTextbooks = Backbone.View.extend({ var ListTextbooks = BaseView.extend({
initialize: function() { initialize: function() {
this.emptyTemplate = _.template($("#no-textbooks-tpl").text()); this.emptyTemplate = _.template($("#no-textbooks-tpl").text());
this.listenTo(this.collection, 'all', this.render); this.listenTo(this.collection, 'all', this.render);
......
define( 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" "js/views/transcripts/metadata_videolist"
], ],
function(Backbone, _, MetadataModel, AbstractEditor, VideoList) { function(BaseView, _, MetadataModel, AbstractEditor, VideoList) {
var Metadata = {}; var Metadata = {};
Metadata.Editor = Backbone.View.extend({ Metadata.Editor = BaseView.extend({
// Model is CMS.Models.MetadataCollection, // Model is CMS.Models.MetadataCollection,
initialize : function() { initialize : function() {
......
define(["backbone", "underscore", "gettext", "js/models/assignment_grade", "js/views/feedback_notification"], define(["js/views/baseview", "underscore", "gettext", "js/models/assignment_grade", "js/views/feedback_notification"],
function(Backbone, _, gettext, AssignmentGrade, NotificationView) { function(BaseView, _, gettext, AssignmentGrade, NotificationView) {
var OverviewAssignmentGrader = Backbone.View.extend({ var OverviewAssignmentGrader = BaseView.extend({
// instantiate w/ { graders : CourseGraderCollection, el : <the gradable-status div> } // instantiate w/ { graders : CourseGraderCollection, el : <the gradable-status div> }
events : { events : {
"click .menu-toggle" : "showGradeMenu", "click .menu-toggle" : "showGradeMenu",
......
define(["backbone", "underscore", "js/views/feedback_prompt", "js/views/section_show", "require"], function(Backbone, _, PromptView, SectionShowView, require) { define(["js/views/baseview", "underscore", "js/views/feedback_prompt", "js/views/section_show", "require"], function(BaseView, _, PromptView, SectionShowView, require) {
var SectionEdit = Backbone.View.extend({ var SectionEdit = BaseView.extend({
render: function() { render: function() {
var attrs = { var attrs = {
name: this.model.escape('name') name: this.model.escape('name')
......
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>'), template: _.template('<span data-tooltip="<%= gettext("Edit this section\'s name") %>" class="section-name-span"><%= name %></span>'),
render: function() { render: function() {
var attrs = { var attrs = {
......
define(["backbone", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt"], define(["js/views/baseview", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt"],
function(Backbone, _, gettext, NotificationView, PromptView) { function(BaseView, _, gettext, NotificationView, PromptView) {
var ShowTextbook = Backbone.View.extend({ var ShowTextbook = BaseView.extend({
initialize: function() { initialize: function() {
this.template = _.template($("#show-textbook-tpl").text()); this.template = _.template($("#show-textbook-tpl").text());
this.listenTo(this.model, "change", this.render); this.listenTo(this.model, "change", this.render);
......
define(["backbone", "underscore", "jquery", "jquery.form"], define(["js/views/baseview", "underscore", "jquery", "jquery.form"],
function(Backbone, _, $) { function(BaseView, _, $) {
var UploadDialog = Backbone.View.extend({ var UploadDialog = BaseView.extend({
options: { options: {
shown: true, shown: true,
successMessageTimeout: 2000 // 2 seconds successMessageTimeout: 2000 // 2 seconds
......
define(["backbone", "underscore", "jquery", "gettext", "js/views/feedback_notification", "js/views/feedback_alert", "jquery.smoothScroll"], define(["js/views/baseview", "underscore", "jquery", "gettext", "js/views/feedback_notification", "js/views/feedback_alert", "js/views/baseview", "jquery.smoothScroll"],
function(Backbone, _, $, gettext, NotificationView, AlertView) { 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 // 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 // decorates the fields. Needs wiring per class, but this initialization shows how
// either have your init call this one or copy the contents // either have your init call this one or copy the contents
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment