Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cms/static/coffee/spec/main.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions cms/static/js/base.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
37 changes: 37 additions & 0 deletions cms/static/js/spec/utils/handle_iframe_binding_spec.js
Original file line number Diff line number Diff line change
@@ -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&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">');
});
});
});
50 changes: 50 additions & 0 deletions cms/static/js/spec/views/baseview_spec.js
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions cms/static/js/utils/handle_iframe_binding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
define(["jquery"], function($) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write a unit test for this class. These lines are currently missing coverage: 10,11,12,13,14,15,16,18.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, you can see unit test coverage by clicking on the green check mark next to your commit number, then selecting "Diff Coverage Report" on the left-hand side.

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
};
});
4 changes: 2 additions & 2 deletions cms/static/js/views/abstract_editor.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/asset.js
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
4 changes: 2 additions & 2 deletions cms/static/js/views/assets.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
44 changes: 44 additions & 0 deletions cms/static/js/views/baseview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
define(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some documentation why this class should be used instead of extending Backbone.View directly.

[
'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;
});
4 changes: 2 additions & 2 deletions cms/static/js/views/checklist.js
Original file line number Diff line number Diff line change
@@ -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 : {
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/course_info_edit.js
Original file line number Diff line number Diff line change
@@ -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',

Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/course_info_handout.js
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
9 changes: 7 additions & 2 deletions cms/static/js/views/course_info_helper.js
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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;
};
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/course_info_update.js
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/edit_chapter.js
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/edit_textbook.js
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
4 changes: 2 additions & 2 deletions cms/static/js/views/feedback.js
Original file line number Diff line number Diff line change
@@ -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: "",
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/list_textbooks.js
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/metadata.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
6 changes: 3 additions & 3 deletions cms/static/js/views/overview_assignment_grader.js
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
4 changes: 2 additions & 2 deletions cms/static/js/views/section_edit.js
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
Loading