Merge pull request #13172 from edx/bjacobel/autofix-lms

Run ESLint autofixer in LMS
This commit is contained in:
Brian Jacobel
2016-08-09 10:12:39 -04:00
committed by GitHub
386 changed files with 13004 additions and 13520 deletions

View File

@@ -1,9 +1,9 @@
;(function (define) {
(function(define) {
'use strict';
define(['jquery', 'underscore', 'support/js/views/certificates'],
function ($, _, CertificatesView) {
return function (options) {
function($, _, CertificatesView) {
return function(options) {
options = _.extend(options, {
el: $('.certificates-content')
});

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'support/js/models/certificate'],
function(Backbone, CertModel) {
@@ -26,5 +26,5 @@
return url;
}
});
});
});
}).call(this, define || RequireJS.define);

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'support/js/models/enrollment'],
function(Backbone, EnrollmentModel) {
@@ -14,5 +14,5 @@
return this.baseUrl + this.user;
}
});
});
});
}).call(this, define || RequireJS.define);

View File

@@ -1,11 +1,11 @@
;(function (define) {
(function(define) {
'use strict';
define([
'underscore',
'support/js/views/enrollment'
], function (_, EnrollmentView) {
return function (options) {
], function(_, EnrollmentView) {
return function(options) {
options = _.extend({el: '.enrollment-content'}, options);
return new EnrollmentView(options).render();
};

View File

@@ -1,6 +1,6 @@
(function (define) {
(function(define) {
'use strict';
define(['backbone'], function (Backbone) {
define(['backbone'], function(Backbone) {
return Backbone.Model.extend({
defaults: {
username: null,

View File

@@ -1,8 +1,8 @@
(function (define) {
(function(define) {
'use strict';
define(['backbone', 'underscore'], function (Backbone, _) {
define(['backbone', 'underscore'], function(Backbone, _) {
return Backbone.Model.extend({
updateEnrollment: function (new_mode, reason) {
updateEnrollment: function(new_mode, reason) {
return $.ajax({
url: this.url(),
type: 'POST',
@@ -13,7 +13,7 @@
old_mode: this.get('mode'),
reason: reason
}),
success: _.bind(function (response) {
success: _.bind(function(response) {
this.set('manual_enrollment', response);
this.set('mode', new_mode);
}, this)

View File

@@ -1,21 +1,21 @@
define([
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/collections/enrollment',
], function (AjaxHelpers, EnrollmentHelpers, EnrollmentCollection) {
'support/js/collections/enrollment'
], function(AjaxHelpers, EnrollmentHelpers, EnrollmentCollection) {
'use strict';
describe('EnrollmentCollection', function () {
describe('EnrollmentCollection', function() {
var enrollmentCollection;
beforeEach(function () {
beforeEach(function() {
enrollmentCollection = new EnrollmentCollection([EnrollmentHelpers.mockEnrollmentData], {
user: 'test-user',
baseUrl: '/support/enrollment/'
});
});
it('sets its URL based on the user', function () {
it('sets its URL based on the user', function() {
expect(enrollmentCollection.url()).toEqual('/support/enrollment/test-user');
});
});

View File

@@ -2,20 +2,20 @@ define([
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/models/enrollment'
], function (AjaxHelpers, EnrollmentHelpers, EnrollmentModel) {
], function(AjaxHelpers, EnrollmentHelpers, EnrollmentModel) {
'use strict';
describe('EnrollmentModel', function () {
describe('EnrollmentModel', function() {
var enrollment;
beforeEach(function () {
beforeEach(function() {
enrollment = new EnrollmentModel(EnrollmentHelpers.mockEnrollmentData);
enrollment.url = function () {
enrollment.url = function() {
return '/support/enrollment/test-user';
};
});
it('can save an enrollment to the server and updates itself on success', function () {
it('can save an enrollment to the server and updates itself on success', function() {
var requests = AjaxHelpers.requests(this),
manual_enrollment = {
'enrolled_by': 'staff@edx.org',
@@ -33,7 +33,7 @@ define([
expect(enrollment.get('manual_enrollment')).toEqual(manual_enrollment);
});
it('does not update itself on a server error', function () {
it('does not update itself on a server error', function() {
var requests = AjaxHelpers.requests(this);
enrollment.updateEnrollment('verified', 'Financial Assistance');
AjaxHelpers.respondWithError(requests, 500);

View File

@@ -6,91 +6,90 @@ define([
'use strict';
describe('CertificatesView', function() {
var view = null,
REGENERATE_SEARCH_RESULTS = [
{
'username': 'student',
'status': 'notpassing',
'created': '2015-08-05T17:32:25+00:00',
'grade': '0.0',
'type': 'honor',
'course_key': 'course-v1:edX+DemoX+Demo_Course',
'download_url': null,
'modified': '2015-08-06T19:47:07+00:00',
'regenerate': true
},
{
'username': 'student',
'status': 'downloadable',
'created': '2015-08-05T17:53:33+00:00',
'grade': '1.0',
'type': 'verified',
'course_key': 'edx/test/2015',
'download_url': 'http://www.example.com/certificate.pdf',
'modified': '2015-08-06T19:47:05+00:00',
'regenerate': true
}
],
REGENERATE_SEARCH_RESULTS = [
{
'username': 'student',
'status': 'notpassing',
'created': '2015-08-05T17:32:25+00:00',
'grade': '0.0',
'type': 'honor',
'course_key': 'course-v1:edX+DemoX+Demo_Course',
'download_url': null,
'modified': '2015-08-06T19:47:07+00:00',
'regenerate': true
},
{
'username': 'student',
'status': 'downloadable',
'created': '2015-08-05T17:53:33+00:00',
'grade': '1.0',
'type': 'verified',
'course_key': 'edx/test/2015',
'download_url': 'http://www.example.com/certificate.pdf',
'modified': '2015-08-06T19:47:05+00:00',
'regenerate': true
}
],
GENERATE_SEARCH_RESULTS = [
{
'username': 'student',
'status': '',
'created': '',
'grade': '',
'type': '',
'course_key': 'edx/test1/2016',
'download_url': null,
'modified': '',
'regenerate': false
}
],
GENERATE_SEARCH_RESULTS = [
{
'username': 'student',
'status': '',
'created': '',
'grade': '',
'type': '',
'course_key': 'edx/test1/2016',
'download_url': null,
'modified': '',
'regenerate': false
}
],
getSearchResults = function() {
var results = [];
getSearchResults = function() {
var results = [];
$('.certificates-results tr').each(function(rowIndex, rowValue) {
var columns = [];
$(rowValue).children('td').each(function(colIndex, colValue) {
columns[colIndex] = $(colValue).html();
$('.certificates-results tr').each(function(rowIndex, rowValue) {
var columns = [];
$(rowValue).children('td').each(function(colIndex, colValue) {
columns[colIndex] = $(colValue).html();
});
if (columns.length > 0) {
results.push(columns);
}
});
if (columns.length > 0) {
results.push(columns);
}
});
return results;
},
return results;
},
searchFor = function(user_filter, course_filter, requests, response) {
searchFor = function(user_filter, course_filter, requests, response) {
// Enter the search term and submit
var url = '/certificates/search?user=' + user_filter;
view.setUserFilter(user_filter);
if (course_filter) {
view.setCourseFilter(course_filter);
url += '&course_id=' + course_filter;
}
view.triggerSearch();
var url = '/certificates/search?user=' + user_filter;
view.setUserFilter(user_filter);
if (course_filter) {
view.setCourseFilter(course_filter);
url += '&course_id=' + course_filter;
}
view.triggerSearch();
// Simulate a response from the server
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
AjaxHelpers.respondWithJson(requests, response);
},
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
AjaxHelpers.respondWithJson(requests, response);
},
regenerateCerts = function(username, courseKey) {
var sel = '.btn-cert-regenerate[data-course-key="' + courseKey + '"]';
$(sel).click();
},
regenerateCerts = function(username, courseKey) {
var sel = '.btn-cert-regenerate[data-course-key="' + courseKey + '"]';
$(sel).click();
},
generateCerts = function(username, courseKey) {
var sel = '.btn-cert-generate[data-course-key="' + courseKey + '"]';
$(sel).click();
};
generateCerts = function(username, courseKey) {
var sel = '.btn-cert-generate[data-course-key="' + courseKey + '"]';
$(sel).click();
};
beforeEach(function () {
beforeEach(function() {
spyOn(window.history, 'pushState');
setFixtures('<div class="certificates-content"></div>');
view = new CertificatesView({
@@ -142,7 +141,6 @@ define([
expect(results[0][3]).toContain('Not available');
expect(results[0][4]).toEqual(GENERATE_SEARCH_RESULTS[0].grade);
expect(results[0][5]).toEqual(GENERATE_SEARCH_RESULTS[0].modified);
});
it('searches for certificates and displays a message when there are no results', function() {
@@ -201,7 +199,7 @@ define([
AjaxHelpers.respondWithJson(requests, '');
});
it('generate a certificate for a student', function() {
it('generate a certificate for a student', function() {
var requests = AjaxHelpers.requests(this);
// Trigger a search
@@ -223,6 +221,5 @@ define([
// Respond with success
AjaxHelpers.respondWithJson(requests, '');
});
});
});

View File

@@ -4,16 +4,15 @@ define([
'support/js/spec_helpers/enrollment_helpers',
'support/js/models/enrollment',
'support/js/views/enrollment_modal'
], function (_, AjaxHelpers, EnrollmentHelpers, EnrollmentModel, EnrollmentModal) {
], function(_, AjaxHelpers, EnrollmentHelpers, EnrollmentModel, EnrollmentModal) {
'use strict';
describe('EnrollmentModal', function () {
describe('EnrollmentModal', function() {
var modal;
beforeEach(function () {
beforeEach(function() {
var enrollment = new EnrollmentModel(EnrollmentHelpers.mockEnrollmentData);
enrollment.url = function () {
enrollment.url = function() {
return '/support/enrollment/test-user';
};
setFixtures('<div class="enrollment-modal-wrapper is-hidden"></div>');
@@ -23,53 +22,53 @@ define([
modes: ['verified', 'audit'],
reasons: _.reduce(
['Financial Assistance', 'Stampeding Buffalo', 'Angry Customer'],
function (acc, x) { acc[x] = x; return acc; },
function(acc, x) { acc[x] = x; return acc; },
{}
)
}).render();
});
it('can render itself', function () {
it('can render itself', function() {
expect($('.enrollment-modal h1').text()).toContain(
'Change enrollment for ' + EnrollmentHelpers.TEST_COURSE
);
expect($('.enrollment-change-field p').first().text()).toContain('Current enrollment mode: audit');
_.each(['verified', 'audit'], function (mode) {
_.each(['verified', 'audit'], function(mode) {
expect($('.enrollment-new-mode').html()).toContain('<option value="' + mode + '">');
});
_.each(['', 'Financial Assistance', 'Stampeding Buffalo', 'Angry Customer'], function (reason) {
_.each(['', 'Financial Assistance', 'Stampeding Buffalo', 'Angry Customer'], function(reason) {
expect($('.enrollment-reason').html()).toContain('<option value="' + reason + '">');
});
});
it('is hidden by default', function () {
it('is hidden by default', function() {
expect($('.enrollment-modal-wrapper')).toHaveClass('is-hidden');
});
it('can show and hide itself', function () {
it('can show and hide itself', function() {
modal.show();
expect($('.enrollment-modal-wrapper')).not.toHaveClass('is-hidden');
modal.hide();
expect($('.enrollment-modal-wrapper')).toHaveClass('is-hidden');
});
it('shows errors on submit if a reason is not given', function () {
it('shows errors on submit if a reason is not given', function() {
expect($('.enrollment-change-errors').css('display')).toEqual('none');
$('.enrollment-change-submit').click();
expect($('.enrollment-change-errors').css('display')).not.toEqual('none');
expect($('.enrollment-change-errors').text()).toContain('Please specify a reason.');
});
it('can does not error if a free-form reason is given', function () {
it('can does not error if a free-form reason is given', function() {
AjaxHelpers.requests(this);
$('.enrollment-reason-other').val('For Fun');
$('.enrollment-change-submit').click();
expect($('.enrollment-change-errors').css('display')).toEqual('none');
});
it('can submit an enrollment change request and hides itself on success', function () {
it('can submit an enrollment change request and hides itself on success', function() {
var requests = AjaxHelpers.requests(this);
$('.enrollment-new-mode').val('verified');
$('.enrollment-reason').val('Financial Assistance');
@@ -87,7 +86,7 @@ define([
expect($('.enrollment-change-errors').css('display')).toEqual('none');
});
it('shows a message on a server error', function () {
it('shows a message on a server error', function() {
var requests = AjaxHelpers.requests(this);
$('.enrollment-new-mode').val('verified');
$('.enrollment-reason').val('Financial Assistance');
@@ -97,7 +96,7 @@ define([
expect($('.enrollment-change-errors').text()).toContain('Something went wrong');
});
it('hides itself on cancel', function () {
it('hides itself on cancel', function() {
var requests = AjaxHelpers.requests(this);
modal.show();
$('.enrollment-change-cancel').click();

View File

@@ -3,11 +3,11 @@ define([
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'support/js/spec_helpers/enrollment_helpers',
'support/js/views/enrollment'
], function (_, AjaxHelpers, EnrollmentHelpers, EnrollmentView) {
], function(_, AjaxHelpers, EnrollmentHelpers, EnrollmentView) {
'use strict';
var enrollmentView,
createEnrollmentView = function (options) {
createEnrollmentView = function(options) {
if (_.isUndefined(options)) {
options = {};
}
@@ -15,22 +15,22 @@ define([
el: '.enrollment-content',
user: 'test-user',
enrollmentsUrl: '/support/enrollment/',
enrollmentSupportUrl: '/support/enrollment/',
enrollmentSupportUrl: '/support/enrollment/'
}, options));
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div class="enrollment-content"></div>');
});
describe('EnrollmentView', function () {
it('can render itself without an initial user', function () {
describe('EnrollmentView', function() {
it('can render itself without an initial user', function() {
enrollmentView = createEnrollmentView({user: ''}).render();
expect($('.enrollment-search input').val()).toBe('');
expect($('.enrollment-results').length).toBe(0);
});
it('renders itself when an initial user is provided', function () {
it('renders itself when an initial user is provided', function() {
var requests = AjaxHelpers.requests(this);
enrollmentView = createEnrollmentView().render();
AjaxHelpers.expectRequest(requests, 'GET', '/support/enrollment/test-user', null);
@@ -43,7 +43,7 @@ define([
});
});
it('re-renders itself when its collection changes', function () {
it('re-renders itself when its collection changes', function() {
var requests = AjaxHelpers.requests(this);
enrollmentView = createEnrollmentView().render();
spyOn(enrollmentView, 'render').and.callThrough();
@@ -51,7 +51,7 @@ define([
expect(enrollmentView.render).toHaveBeenCalled();
});
it('shows a modal dialog to change enrollments', function () {
it('shows a modal dialog to change enrollments', function() {
var requests = AjaxHelpers.requests(this);
enrollmentView = createEnrollmentView().render();
AjaxHelpers.respondWithJson(requests, [EnrollmentHelpers.mockEnrollmentData]);

View File

@@ -1,36 +1,36 @@
define([], function () {
define([], function() {
'use strict';
var testCourse = "course-v1:TestX+T101+2015";
var testCourse = 'course-v1:TestX+T101+2015';
return {
TEST_COURSE: testCourse,
mockEnrollmentData: {
created: "2015-12-07T18:17:46.210940Z",
mode: "audit",
created: '2015-12-07T18:17:46.210940Z',
mode: 'audit',
is_active: true,
user: "test-user",
course_end: "2017-01-01T00:00:00Z",
course_start: "2015-01-01T00:00:00Z",
user: 'test-user',
course_end: '2017-01-01T00:00:00Z',
course_start: '2015-01-01T00:00:00Z',
course_modes: [
{
slug: "audit",
name: "Audit",
slug: 'audit',
name: 'Audit',
min_price: 0,
suggested_prices: "",
currency: "usd",
suggested_prices: '',
currency: 'usd',
expiration_datetime: null,
description: null,
sku: "6ED7EDC"
sku: '6ED7EDC'
},
{
slug: "verified",
name: "Verified Certificate",
slug: 'verified',
name: 'Verified Certificate',
min_price: 5,
suggested_prices: "",
currency: "usd",
suggested_prices: '',
currency: 'usd',
expiration_datetime: null,
description: null,
sku: "25A5354"
sku: '25A5354'
}
],
enrollment_start: null,

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define([
@@ -8,7 +8,7 @@
'support/js/collections/certificate',
'text!support/templates/certificates.underscore',
'text!support/templates/certificates_results.underscore'
], function (Backbone, _, gettext, CertCollection, certificatesTpl, resultsTpl) {
], function(Backbone, _, gettext, CertCollection, certificatesTpl, resultsTpl) {
return Backbone.View.extend({
events: {
'submit .certificates-form': 'search',
@@ -53,11 +53,10 @@
},
search: function(event) {
// Fetch the certificate collection for the given user
var url = '/support/certificates?user=' + this.getUserFilter();
//course id is optional.
// course id is optional.
if (this.getCourseFilter()) {
url += '&course_id=' + this.getCourseFilter();
}
@@ -171,13 +170,13 @@
},
setResults: function(html) {
$(".certificates-results", this.$el).html(html);
$('.certificates-results', this.$el).html(html);
},
disableButtons: function() {
$('.btn-disable-on-submit')
.addClass("is-disabled")
.attr("disabled", true);
.addClass('is-disabled')
.attr('disabled', true);
},
enableButtons: function() {

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define([
@@ -8,7 +8,7 @@
'support/js/views/enrollment_modal',
'support/js/collections/enrollment',
'text!support/templates/enrollment.underscore'
], function (Backbone, _, moment, EnrollmentModal, EnrollmentCollection, enrollmentTemplate) {
], function(Backbone, _, moment, EnrollmentModal, EnrollmentCollection, enrollmentTemplate) {
return Backbone.View.extend({
ENROLLMENT_CHANGE_REASONS: {
@@ -22,7 +22,7 @@
'click .change-enrollment-btn': 'changeEnrollment'
},
initialize: function (options) {
initialize: function(options) {
var user = options.user;
this.initialUser = user;
this.enrollmentSupportUrl = options.enrollmentSupportUrl;
@@ -33,12 +33,12 @@
this.enrollments.on('change', _.bind(this.render, this));
},
render: function () {
render: function() {
var user = this.enrollments.user;
this.$el.html(_.template(enrollmentTemplate)({
user: user,
enrollments: this.enrollments,
formatDate: function (date) {
formatDate: function(date) {
if (!date) {
return 'N/A';
}
@@ -56,7 +56,7 @@
* Check if the URL has provided an initial search, and
* perform that search if so.
*/
checkInitialSearch: function () {
checkInitialSearch: function() {
if (this.initialUser) {
delete this.initialUser;
this.$('.enrollment-form').submit();
@@ -66,18 +66,18 @@
/*
* Return the user's search string.
*/
getSearchString: function () {
getSearchString: function() {
return this.$('#enrollment-query-input').val();
},
/*
* Perform the search. Renders the view on success.
*/
search: function (event) {
search: function(event) {
event.preventDefault();
this.enrollments.user = this.getSearchString();
this.enrollments.fetch({
success: _.bind(function () {
success: _.bind(function() {
this.render();
}, this)
});
@@ -87,7 +87,7 @@
* Show a modal view allowing the user to change a
* learner's enrollment.
*/
changeEnrollment: function (event) {
changeEnrollment: function(event) {
var button = $(event.currentTarget),
course_id = button.data('course_id'),
modes = button.data('modes').split(','),

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define([
@@ -6,44 +6,44 @@
'underscore',
'gettext',
'text!support/templates/enrollment-modal.underscore'
], function (Backbone, _, gettext, modalTemplate) {
], function(Backbone, _, gettext, modalTemplate) {
var EnrollmentModal = Backbone.View.extend({
events: {
'click .enrollment-change-submit': 'submitEnrollmentChange',
'click .enrollment-change-cancel': 'cancel'
},
initialize: function (options) {
initialize: function(options) {
this.enrollment = options.enrollment;
this.modes = options.modes;
this.reasons = options.reasons;
this.template = modalTemplate;
},
render: function () {
render: function() {
this.$el.html(_.template(this.template)({
enrollment: this.enrollment,
modes: this.modes,
reasons: this.reasons,
reasons: this.reasons
}));
return this;
},
show: function () {
show: function() {
this.$el.removeClass('is-hidden').addClass('is-shown');
this.render();
},
hide: function () {
hide: function() {
this.$el.removeClass('is-shown').addClass('is-hidden');
this.render();
},
showErrors: function (errorMessage) {
showErrors: function(errorMessage) {
this.$('.enrollment-change-errors').text(errorMessage).css('display', '');
},
submitEnrollmentChange: function (event) {
submitEnrollmentChange: function(event) {
var new_mode = this.$('.enrollment-new-mode').val(),
reason = this.$('.enrollment-reason').val() || this.$('.enrollment-reason-other').val();
event.preventDefault();
@@ -53,11 +53,11 @@
else {
this.enrollment.updateEnrollment(new_mode, reason).then(
// Success callback
_.bind(function () {
_.bind(function() {
this.hide();
}, this),
// Error callback
_.bind(function () {
_.bind(function() {
this.showErrors(gettext(
'Something went wrong changing this enrollment. Please try again.'
));
@@ -66,7 +66,7 @@
}
},
cancel: function (event) {
cancel: function(event) {
event.preventDefault();
this.hide();
}

View File

@@ -1,9 +1,9 @@
;(function (define) {
(function(define) {
'use strict';
define(['edx-ui-toolkit/js/pagination/paging-collection'],
function(PagingCollection) {
var BaseCollection = PagingCollection.extend({
constructor: function (models, options) {
constructor: function(models, options) {
this.options = options;
this.url = options.url;
if (options.perPage) {
@@ -17,8 +17,8 @@
this.queryParams = _.extend({}, BaseCollection.prototype.queryParams, this.queryParams);
PagingCollection.prototype.constructor.call(this, models, options);
},
parse: function (response, options) {
parse: function(response, options) {
if (!response) {
response = {};
}
@@ -37,11 +37,11 @@
// TODO: These changes has been added to backbone.paginator
// remove when backbone.paginator gets a new release
sync: function (method, model, options) {
sync: function(method, model, options) {
// do not send total pages and total records in request
if (method === 'read') {
var params = _.values(_.pick(this.queryParams, ['totalPages', 'totalRecords']));
_.each(params, function (param) {
_.each(params, function(param) {
delete options.data[param];
});
}

View File

@@ -1,17 +1,17 @@
;(function (define) {
(function(define) {
'use strict';
define(['teams/js/collections/team'], function (TeamCollection) {
define(['teams/js/collections/team'], function(TeamCollection) {
var MyTeamsCollection = TeamCollection.extend({
queryParams: {
username: function () {
username: function() {
return this.options.username;
},
text_search: function () {
text_search: function() {
return this.searchString || '';
}
},
constructor: function (teams, options) {
constructor: function(teams, options) {
TeamCollection.prototype.constructor.call(this, teams, options);
delete this.queryParams.topic_id;
}

View File

@@ -1,27 +1,27 @@
;(function (define) {
(function(define) {
'use strict';
define(['teams/js/collections/base', 'teams/js/models/team', 'gettext'],
function(BaseCollection, TeamModel, gettext) {
var TeamCollection = BaseCollection.extend({
model: TeamModel,
state: {
sortKey: 'last_activity_at',
order: null
},
queryParams: {
topic_id: function () {
topic_id: function() {
return this.options.topic_id;
},
expand: 'user',
course_id: function () {
course_id: function() {
return this.options.course_id;
},
order_by: function () {
order_by: function() {
return this.getSearchString() ? '' : this.state.sortKey;
},
text_search: function () {
text_search: function() {
return this.getSearchString() || '';
}
},
@@ -36,10 +36,10 @@
this.registerSortableField('open_slots', gettext('open slots'));
},
setFilterField: function (fieldName, value) {
setFilterField: function(fieldName, value) {
BaseCollection.prototype.setFilterField.call(this, fieldName, value);
this.queryParams[fieldName] = function () {
this.queryParams[fieldName] = function() {
return value;
};
}

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['underscore', 'gettext', 'teams/js/collections/base', 'teams/js/models/topic'],
function(_, gettext, BaseCollection, TopicModel) {
@@ -10,8 +10,8 @@
},
queryParams: {
course_id: function () { return this.course_id; },
text_search: function () { return this.searchString || ''; }
course_id: function() { return this.course_id; },
text_search: function() { return this.searchString || ''; }
},
constructor: function(topics, options) {

View File

@@ -1,9 +1,9 @@
/**
* Model for a team.
*/
(function (define) {
(function(define) {
'use strict';
define(['backbone'], function (Backbone) {
define(['backbone'], function(Backbone) {
var Team = Backbone.Model.extend({
defaults: {
id: null,

View File

@@ -1,9 +1,9 @@
/**
* Model for a team membership.
*/
(function (define) {
(function(define) {
'use strict';
define(['backbone', 'teams/js/models/team'], function (Backbone, TeamModel) {
define(['backbone', 'teams/js/models/team'], function(Backbone, TeamModel) {
var TeamMembership = Backbone.Model.extend({
defaults: {
date_joined: '',
@@ -12,7 +12,7 @@
user: null
},
parse: function (response) {
parse: function(response) {
response.team = new TeamModel(response.team);
return response;
}

View File

@@ -1,9 +1,9 @@
/**
* Model for a topic.
*/
(function (define) {
(function(define) {
'use strict';
define(['backbone'], function (Backbone) {
define(['backbone'], function(Backbone) {
var Topic = Backbone.Model.extend({
defaults: {
name: '',

View File

@@ -1,14 +1,14 @@
define(['backbone', 'URI', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'teams/js/spec_helpers/team_spec_helpers'],
function (Backbone, URI, _, AjaxHelpers, TeamSpecHelpers) {
function(Backbone, URI, _, AjaxHelpers, TeamSpecHelpers) {
'use strict';
describe('TopicCollection', function () {
describe('TopicCollection', function() {
var topicCollection;
beforeEach(function () {
beforeEach(function() {
topicCollection = TeamSpecHelpers.createMockTopicCollection();
});
var testRequestParam = function (self, param, value) {
var testRequestParam = function(self, param, value) {
var requests = AjaxHelpers.requests(self),
request,
url,
@@ -20,25 +20,25 @@ define(['backbone', 'URI', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/a
expect(params[param]).toBe(value);
};
it('sets its page size based on initial page size', function () {
it('sets its page size based on initial page size', function() {
expect(topicCollection.getPageSize()).toBe(5);
expect(topicCollection.getTotalPages()).toBe(2);
});
it('sorts by name', function () {
it('sorts by name', function() {
testRequestParam(this, 'order_by', 'name');
});
it('passes a course_id to the server', function () {
it('passes a course_id to the server', function() {
testRequestParam(this, 'course_id', TeamSpecHelpers.testCourseID);
});
it('URL encodes its course_id ', function () {
it('URL encodes its course_id ', function() {
topicCollection.course_id = 'my+course+id';
testRequestParam(this, 'course_id', 'my+course+id');
});
it('sets itself to stale on receiving a teams create or delete event', function () {
it('sets itself to stale on receiving a teams create or delete event', function() {
expect(topicCollection.isStale).toBe(false);
TeamSpecHelpers.triggerTeamEvent('create');
expect(topicCollection.isStale).toBe(true);

View File

@@ -3,7 +3,7 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory', 'teams/js/views/team
function($, Backbone, TeamsTabFactory, TeamsTabView, PageHelpers, TeamSpecHelpers) {
'use strict';
describe("Teams Tab Factory", function() {
describe('Teams Tab Factory', function() {
var initializeTeamsTabFactory = function() {
TeamsTabFactory(TeamSpecHelpers.createMockContext());
};

View File

@@ -7,7 +7,7 @@ define([
'teams/js/models/team',
'teams/js/views/team_utils',
'teams/js/spec_helpers/team_spec_helpers'
], function ($, _, Backbone, AjaxHelpers, TeamEditMembershipView, TeamModel, TeamUtils, TeamSpecHelpers) {
], function($, _, Backbone, AjaxHelpers, TeamEditMembershipView, TeamModel, TeamUtils, TeamSpecHelpers) {
'use strict';
describe('CreateEditTeam', function() {
@@ -19,13 +19,13 @@ define([
'profile_image': {
'has_image': true,
'image_url_medium': '/frodo-image-url'
},
}
},
last_activity_at: "2015-08-21T18:53:01.145Z",
date_joined: "2014-01-01T18:53:01.145Z"
last_activity_at: '2015-08-21T18:53:01.145Z',
date_joined: '2014-01-01T18:53:01.145Z'
}
],
deleteTeamMemember = function (view, confirm) {
deleteTeamMemember = function(view, confirm) {
view.$('.action-remove-member').click();
// Confirm delete dialog
if (confirm) {
@@ -35,18 +35,18 @@ define([
$('.action-secondary').click();
}
},
verifyTeamMembersView = function (view) {
verifyTeamMembersView = function(view) {
expect(view.$('.team-member').length).toEqual(1);
expect(view.$('.member-profile').attr('href')).toEqual('/u/frodo');
expect(view.$('img.image-url').attr('src')).toEqual('/frodo-image-url');
expect(view.$('.member-info-container .primary').text()).toBe('frodo');
expect(view.$el.find('#last-active abbr').attr('title')).toEqual("2015-08-21T18:53:01.145Z");
expect(view.$el.find('#date-joined abbr').attr('title')).toEqual("2014-01-01T18:53:01.145Z");
expect(view.$el.find('#last-active abbr').attr('title')).toEqual('2015-08-21T18:53:01.145Z');
expect(view.$el.find('#date-joined abbr').attr('title')).toEqual('2014-01-01T18:53:01.145Z');
},
verifyNoMembersView = function (view){
verifyNoMembersView = function(view) {
expect(view.$el.text().trim()).toBe('This team does not have any members.');
},
createTeamModelData = function (membership) {
createTeamModelData = function(membership) {
return {
id: editTeamID,
name: 'Avengers',
@@ -57,10 +57,10 @@ define([
url: '/api/team/v0/teams/' + editTeamID
};
},
createEditTeamMembersView = function (membership) {
createEditTeamMembersView = function(membership) {
var teamModel = new TeamModel(
createTeamModelData(membership),
{ parse: true }
{parse: true}
);
return new TeamEditMembershipView({
@@ -71,18 +71,18 @@ define([
}).render();
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div id="page-prompt"></div><div class="teams-content"></div>');
spyOn(Backbone.history, 'navigate');
spyOn(TeamUtils, 'showMessage');
});
it('can render a message when there are no members', function () {
it('can render a message when there are no members', function() {
var view = createEditTeamMembersView([]);
verifyNoMembersView(view);
});
it('can delete a team member and update the view', function () {
it('can delete a team member and update the view', function() {
var requests = AjaxHelpers.requests(this),
view = createEditTeamMembersView(DEFAULT_MEMBERSHIP);
@@ -91,8 +91,8 @@ define([
deleteTeamMemember(view, true);
AjaxHelpers.expectJsonRequest(
requests,
'DELETE',
requests,
'DELETE',
'/api/team/v0/team_membership/av,frodo?admin=true'
);
AjaxHelpers.respondWithNoContent(requests);
@@ -108,7 +108,7 @@ define([
verifyNoMembersView(view);
});
it('can show an error message if removing the user fails', function () {
it('can show an error message if removing the user fails', function() {
var requests = AjaxHelpers.requests(this),
view = createEditTeamMembersView(DEFAULT_MEMBERSHIP);
@@ -117,9 +117,9 @@ define([
deleteTeamMemember(view, true);
AjaxHelpers.expectJsonRequest(
requests,
'DELETE',
'/api/team/v0/team_membership/av,frodo?admin=true'
requests,
'DELETE',
'/api/team/v0/team_membership/av,frodo?admin=true'
);
AjaxHelpers.respondWithError(requests);
expect(TeamUtils.showMessage).toHaveBeenCalledWith(
@@ -130,7 +130,7 @@ define([
verifyTeamMembersView(view);
});
it('can cancel team membership deletion', function () {
it('can cancel team membership deletion', function() {
var requests = AjaxHelpers.requests(this);
var view = createEditTeamMembersView(DEFAULT_MEMBERSHIP);

View File

@@ -7,7 +7,7 @@ define([
'teams/js/views/edit_team',
'teams/js/models/team',
'teams/js/spec_helpers/team_spec_helpers'
], function ($, _, Backbone, AjaxHelpers, PageHelpers, TeamEditView, TeamModel, TeamSpecHelpers) {
], function($, _, Backbone, AjaxHelpers, PageHelpers, TeamEditView, TeamModel, TeamSpecHelpers) {
'use strict';
describe('CreateEditTeam', function() {
@@ -30,8 +30,8 @@ define([
country: 'US',
language: 'en'
},
verifyValidation = function (requests, teamEditView, fieldsData) {
_.each(fieldsData, function (fieldData) {
verifyValidation = function(requests, teamEditView, fieldsData) {
_.each(fieldsData, function(fieldData) {
teamEditView.$(fieldData[0]).val(fieldData[1]);
});
@@ -47,7 +47,7 @@ define([
'Check the highlighted fields below and try again.'
);
_.each(fieldsData, function (fieldData) {
_.each(fieldsData, function(fieldData) {
if (fieldData[2] === 'error') {
expect(teamEditView.$(fieldData[0].split(' ')[0] + '.error').length).toBe(1);
} else if (fieldData[2] === 'success') {
@@ -56,12 +56,11 @@ define([
});
AjaxHelpers.expectNoRequests(requests);
},
editTeamID = 'av',
teamAction;
var createEditTeamView = function () {
var createEditTeamView = function() {
var testTeam = {};
if (teamAction === 'edit') {
testTeam = new TeamModel(
@@ -90,7 +89,7 @@ define([
}).render();
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div class="teams-content"></div>');
PageHelpers.preventBackboneChangingUrl();
spyOn(Backbone.history, 'navigate');
@@ -106,7 +105,7 @@ define([
],
teamEditView = createEditTeamView();
_.each(fieldClasses, function (fieldClass) {
_.each(fieldClasses, function(fieldClass) {
expect(teamEditView.$el.find(fieldClass).length).toBe(1);
});
@@ -122,7 +121,7 @@ define([
};
var requestMethod = function() {
return teamAction === 'create' ? 'POST' : 'PATCH';
return teamAction === 'create' ? 'POST' : 'PATCH';
};
var assertTeamCreateUpdateInfo = function(that, teamsData, teamsUrl, expectedUrl) {
@@ -220,54 +219,52 @@ define([
expect(Backbone.history.navigate.calls.mostRecent().args[0]).toBe(expectedUrl);
};
describe('NewTeam', function () {
describe('NewTeam', function() {
beforeEach(function() {
teamAction = 'create';
});
it('can render itself correctly', function () {
it('can render itself correctly', function() {
assertFormRendersCorrectly();
});
it('can create a team', function () {
it('can create a team', function() {
assertTeamCreateUpdateInfo(
this, createTeamData, teamsUrl, 'teams/' + TeamSpecHelpers.testTopicID + '/123'
);
});
it('shows validation error message when field is empty', function () {
it('shows validation error message when field is empty', function() {
assertValidationMessagesWhenFieldsEmpty(this);
});
it('shows validation error message when field value length exceeded the limit', function () {
it('shows validation error message when field value length exceeded the limit', function() {
assertValidationMessagesWhenInvalidData(this);
});
it('shows an error message for HTTP 500', function () {
it('shows an error message for HTTP 500', function() {
assertShowMessageOnError(this, createTeamData, teamsUrl, 500);
});
it('shows correct error message when server returns an error', function () {
it('shows correct error message when server returns an error', function() {
assertShowMessageOnError(this, createTeamData, teamsUrl, 400);
});
it('changes route on cancel click', function () {
it('changes route on cancel click', function() {
assertRedirectsToCorrectUrlOnCancel('topics/' + TeamSpecHelpers.testTopicID);
});
});
describe('EditTeam', function () {
describe('EditTeam', function() {
beforeEach(function() {
teamAction = 'edit';
});
it('can render itself correctly', function () {
it('can render itself correctly', function() {
assertFormRendersCorrectly();
});
it('can edit a team', function () {
it('can edit a team', function() {
var copyTeamsData = _.clone(editTeamData);
copyTeamsData.country = 'CA';
copyTeamsData.language = 'fr';
@@ -278,23 +275,23 @@ define([
);
});
it('shows validation error message when field is empty', function () {
it('shows validation error message when field is empty', function() {
assertValidationMessagesWhenFieldsEmpty(this);
});
it('shows validation error message when field value length exceeded the limit', function () {
it('shows validation error message when field value length exceeded the limit', function() {
assertValidationMessagesWhenInvalidData(this);
});
it('shows an error message for HTTP 500', function () {
it('shows an error message for HTTP 500', function() {
assertShowMessageOnError(this, editTeamData, teamsUrl + editTeamID + '?expand=user', 500);
});
it('shows correct error message when server returns an error', function () {
it('shows correct error message when server returns an error', function() {
assertShowMessageOnError(this, editTeamData, teamsUrl + editTeamID + '?expand=user', 400);
});
it('changes route on cancel click', function () {
it('changes route on cancel click', function() {
assertRedirectsToCorrectUrlOnCancel('teams/' + TeamSpecHelpers.testTopicID + '/' + editTeamID);
});
});

View File

@@ -8,18 +8,18 @@ define([
'teams/js/spec_helpers/team_spec_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/page_helpers'
], function ($, Backbone, _, Team, InstructorToolsView, TeamUtils, TeamSpecHelpers, AjaxHelpers, PageHelpers) {
], function($, Backbone, _, Team, InstructorToolsView, TeamUtils, TeamSpecHelpers, AjaxHelpers, PageHelpers) {
'use strict';
describe('Instructor Tools', function () {
describe('Instructor Tools', function() {
var view,
createInstructorTools = function () {
createInstructorTools = function() {
return new InstructorToolsView({
team: new Team(TeamSpecHelpers.createMockTeamData(1, 1)[0]),
teamEvents: TeamSpecHelpers.teamEvents
});
},
deleteTeam = function (view, confirm) {
deleteTeam = function(view, confirm) {
view.$('.action-delete').click();
// Confirm delete dialog
if (confirm) {
@@ -29,14 +29,14 @@ define([
$('.action-secondary').click();
}
},
expectSuccessMessage = function (team) {
expectSuccessMessage = function(team) {
expect(TeamUtils.showMessage).toHaveBeenCalledWith(
'Team "' + team.get('name') + '" successfully deleted.',
'success'
);
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div id="page-prompt"></div>');
PageHelpers.preventBackboneChangingUrl();
spyOn(Backbone.history, 'navigate');
@@ -45,13 +45,13 @@ define([
spyOn(view.teamEvents, 'trigger');
});
it('can render itself', function () {
it('can render itself', function() {
expect(_.strip(view.$('.action-delete').text())).toEqual('Delete Team');
expect(_.strip(view.$('.action-edit-members').text())).toEqual('Edit Membership');
expect(view.$el.text()).toContain('Instructor tools');
});
it('can delete a team and shows a success message', function () {
it('can delete a team and shows a success message', function() {
var requests = AjaxHelpers.requests(this);
deleteTeam(view, true);
AjaxHelpers.expectJsonRequest(requests, 'DELETE', view.team.url, null);
@@ -69,24 +69,24 @@ define([
expectSuccessMessage(view.team);
});
it('can cancel team deletion', function () {
it('can cancel team deletion', function() {
var requests = AjaxHelpers.requests(this);
deleteTeam(view, false);
AjaxHelpers.expectNoRequests(requests);
expect(Backbone.history.navigate).not.toHaveBeenCalled();
});
it('shows a success message after receiving a 404', function () {
it('shows a success message after receiving a 404', function() {
var requests = AjaxHelpers.requests(this);
deleteTeam(view, true);
AjaxHelpers.respondWithError(requests, 404);
expectSuccessMessage(view.team);
});
it('can trigger the edit membership view', function () {
});
it('can trigger the edit membership view', function() {
view.$('.action-edit-members').click();
expect(Backbone.history.navigate).toHaveBeenCalledWith(
'teams/' + view.team.get('topic_id') + "/" + view.team.id + "/edit-team/manage-members",
'teams/' + view.team.get('topic_id') + '/' + view.team.id + '/edit-team/manage-members',
{trigger: true}
);
});

View File

@@ -4,10 +4,10 @@ define([
'teams/js/views/my_teams',
'teams/js/spec_helpers/team_spec_helpers',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
], function (Backbone, MyTeamsCollection, MyTeamsView, TeamSpecHelpers, AjaxHelpers) {
], function(Backbone, MyTeamsCollection, MyTeamsView, TeamSpecHelpers, AjaxHelpers) {
'use strict';
describe('My Teams View', function () {
beforeEach(function () {
describe('My Teams View', function() {
beforeEach(function() {
setFixtures('<div class="teams-container"></div>');
});
@@ -20,7 +20,7 @@ define([
}).render();
};
it('can render itself', function () {
it('can render itself', function() {
var teamsData = TeamSpecHelpers.createMockTeamData(1, 5),
teams = TeamSpecHelpers.createMockTeams({results: teamsData}),
myTeamsView = createMyTeamsView(teams);
@@ -31,7 +31,7 @@ define([
expect(myTeamsView.$('.teams-paging-footer').text().trim()).toBe('');
});
it('shows a message when the user is not a member of any teams', function () {
it('shows a message when the user is not a member of any teams', function() {
var teams = TeamSpecHelpers.createMockTeams({results: []}),
myTeamsView = createMyTeamsView(teams);
TeamSpecHelpers.verifyCards(myTeamsView, []);
@@ -49,17 +49,17 @@ define([
myTeamsView = createMyTeamsView(teams);
TeamSpecHelpers.verifyCards(myTeamsView, []);
expect(myTeamsView.$el.text().trim()).toBe('You are not currently a member of any team.');
TeamSpecHelpers.teamEvents.trigger('teams:update', { action: 'create' });
TeamSpecHelpers.teamEvents.trigger('teams:update', {action: 'create'});
myTeamsView.render();
AjaxHelpers.expectRequestURL(
requests,
TeamSpecHelpers.testContext.myTeamsUrl,
{
expand : 'user',
username : TeamSpecHelpers.testContext.userInfo.username,
course_id : TeamSpecHelpers.testContext.courseID,
page : '1',
page_size : '5',
expand: 'user',
username: TeamSpecHelpers.testContext.userInfo.username,
course_id: TeamSpecHelpers.testContext.courseID,
page: '1',
page_size: '5',
text_search: '',
order_by: 'last_activity_at'
}

View File

@@ -3,12 +3,12 @@ define(['jquery',
'moment-with-locales',
'teams/js/views/team_card',
'teams/js/models/team'],
function ($, _, moment, TeamCardView, Team) {
function($, _, moment, TeamCardView, Team) {
'use strict';
describe('TeamCardView', function () {
describe('TeamCardView', function() {
var createTeamCardView, view;
createTeamCardView = function () {
createTeamCardView = function() {
var model = new Team({
id: 'test-team',
name: 'Test Team',
@@ -16,7 +16,7 @@ define(['jquery',
course_id: 'test/course/id',
topic_id: 'test-topic',
description: 'A team for testing',
last_activity_at: "2015-08-21T18:53:01.145Z",
last_activity_at: '2015-08-21T18:53:01.145Z',
country: 'us',
language: 'en',
membership: []
@@ -35,13 +35,13 @@ define(['jquery',
});
};
beforeEach(function () {
beforeEach(function() {
moment.locale('en');
view = createTeamCardView();
view.render();
});
it('can render itself', function () {
it('can render itself', function() {
expect(view.$el).toHaveClass('list-card');
expect(view.$el.find('.card-title').text()).toContain('Test Team');
expect(view.$el.find('.card-description').text()).toContain('A team for testing');
@@ -52,11 +52,11 @@ define(['jquery',
expect(view.$el.find('.team-language').text()).toContain('English');
});
it('navigates to the associated team page when its action button is clicked', function () {
it('navigates to the associated team page when its action button is clicked', function() {
expect(view.$('.action').attr('href')).toEqual('#teams/test-topic/test-team');
});
describe('Profile Image Thumbnails', function () {
describe('Profile Image Thumbnails', function() {
/**
* Takes an array of objects representing team
* members, each having the keys 'username',
@@ -66,9 +66,9 @@ define(['jquery',
*/
var setMemberships, expectThumbnailsOrder;
setMemberships = function (memberships) {
setMemberships = function(memberships) {
view.model.set({
membership: _.map(memberships, function (m) {
membership: _.map(memberships, function(m) {
return {
user: {username: m.username, profile_image: {image_url_small: m.image_url}},
last_activity_at: m.last_activity
@@ -85,58 +85,58 @@ define(['jquery',
* rendered on the team card match, in order, the
* members of the provided list.
*/
expectThumbnailsOrder = function (members) {
expectThumbnailsOrder = function(members) {
var thumbnails = view.$('.item-member-thumb img');
expect(thumbnails.length).toBe(members.length);
thumbnails.each(function (index, imgEl) {
thumbnails.each(function(index, imgEl) {
expect(thumbnails.eq(index).attr('alt')).toBe(members[index].username);
expect(thumbnails.eq(index).attr('src')).toBe(members[index].image_url);
});
};
it('displays no thumbnails for an empty team', function () {
it('displays no thumbnails for an empty team', function() {
view.model.set({membership: []});
view.render();
expect(view.$('.item-member-thumb').length).toBe(0);
});
it('displays thumbnails for a nonempty team', function () {
it('displays thumbnails for a nonempty team', function() {
var users = [
{
username: 'user_1', image_url: 'user_1_image',
last_activity: new Date("2010/1/1").toString()
last_activity: new Date('2010/1/1').toString()
}, {
username: 'user_2', image_url: 'user_2_image',
last_activity: new Date("2011/1/1").toString()
last_activity: new Date('2011/1/1').toString()
}
];
setMemberships(users);
expectThumbnailsOrder([
{username: 'user_2', image_url: 'user_2_image'},
{username: 'user_1', image_url: 'user_1_image'},
{username: 'user_1', image_url: 'user_1_image'}
]);
});
it('displays thumbnails and an ellipsis for a team with greater than 5 members', function () {
it('displays thumbnails and an ellipsis for a team with greater than 5 members', function() {
var users = [
{
username: 'user_1', image_url: 'user_1_image',
last_activity: new Date("2001/1/1").toString()
last_activity: new Date('2001/1/1').toString()
}, {
username: 'user_2', image_url: 'user_2_image',
last_activity: new Date("2006/1/1").toString()
last_activity: new Date('2006/1/1').toString()
}, {
username: 'user_3', image_url: 'user_3_image',
last_activity: new Date("2003/1/1").toString()
last_activity: new Date('2003/1/1').toString()
}, {
username: 'user_4', image_url: 'user_4_image',
last_activity: new Date("2002/1/1").toString()
last_activity: new Date('2002/1/1').toString()
}, {
username: 'user_5', image_url: 'user_5_image',
last_activity: new Date("2005/1/1").toString()
last_activity: new Date('2005/1/1').toString()
}, {
username: 'user_6', image_url: 'user_6_image',
last_activity: new Date("2004/1/1").toString()
last_activity: new Date('2004/1/1').toString()
}
];
setMemberships(users);

View File

@@ -2,7 +2,7 @@ define([
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/views/team_discussion',
'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper'
], function (_, AjaxHelpers, TeamDiscussionView, TeamSpecHelpers, DiscussionSpecHelper) {
], function(_, AjaxHelpers, TeamDiscussionView, TeamSpecHelpers, DiscussionSpecHelper) {
'use strict';
xdescribe('TeamDiscussionView', function() {
var discussionView, createDiscussionView, createPost, expandReplies, postReply;
@@ -38,9 +38,9 @@ define([
};
createPost = function(requests, view, title, body, threadID) {
title = title || "Test title";
body = body || "Test body";
threadID = threadID || "999";
title = title || 'Test title';
body = body || 'Test body';
threadID = threadID || '999';
view.$('.new-post-button').click();
view.$('.js-post-title').val(title);
view.$('.js-post-body textarea').val(body);
@@ -83,7 +83,7 @@ define([
{
courseID: TeamSpecHelpers.testCourseID,
discussionID: TeamSpecHelpers.testTeamDiscussionID,
threadID: threadID || "999"
threadID: threadID || '999'
},
true
)
@@ -104,7 +104,7 @@ define([
'/courses/%(courseID)s/discussion/threads/%(threadID)s/reply?ajax=1',
{
courseID: TeamSpecHelpers.testCourseID,
threadID: threadID || "999"
threadID: threadID || '999'
},
true
),
@@ -115,7 +115,7 @@ define([
body: reply,
comments_count: 1
}),
"annotated_content_info": TeamSpecHelpers.createAnnotatedContentInfo()
'annotated_content_info': TeamSpecHelpers.createAnnotatedContentInfo()
});
};
@@ -143,8 +143,8 @@ define([
it('can post a reply', function() {
var requests = AjaxHelpers.requests(this),
view = createDiscussionView(requests),
testReply = "Test reply",
testThreadID = "1";
testReply = 'Test reply',
testThreadID = '1';
expandReplies(requests, view, testThreadID);
postReply(requests, view, testReply, testThreadID);
expect(view.$('.discussion-response .response-body').text().trim()).toBe(testReply);
@@ -153,7 +153,7 @@ define([
it('can post a reply to a new post', function() {
var requests = AjaxHelpers.requests(this),
view = createDiscussionView(requests, []),
testReply = "Test reply";
testReply = 'Test reply';
createPost(requests, view);
expandReplies(requests, view);
postReply(requests, view, testReply);
@@ -166,7 +166,7 @@ define([
postTopicButton, updatedThreadElement,
updatedTitle = 'Updated title',
updatedBody = 'Updated body',
testThreadID = "1";
testThreadID = '1';
expandReplies(requests, view, testThreadID);
view.$('.action-more .icon').first().click();
view.$('.action-edit').first().click();
@@ -189,7 +189,7 @@ define([
);
AjaxHelpers.respondWithJson(requests, {
content: TeamSpecHelpers.createMockPostResponse({
id: "999", title: updatedTitle, body: updatedBody
id: '999', title: updatedTitle, body: updatedBody
}),
annotated_content_info: TeamSpecHelpers.createAnnotatedContentInfo()
});

View File

@@ -1,10 +1,10 @@
define([
'backbone', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/models/team',
'teams/js/views/team_profile_header_actions', 'teams/js/spec_helpers/team_spec_helpers'
], function (Backbone, _, AjaxHelpers, TeamModel, TeamProfileHeaderActionsView, TeamSpecHelpers) {
], function(Backbone, _, AjaxHelpers, TeamModel, TeamProfileHeaderActionsView, TeamSpecHelpers) {
'use strict';
describe('TeamProfileHeaderActionsView', function () {
describe('TeamProfileHeaderActionsView', function() {
var createTeamsUrl,
createTeamModelData,
createMembershipData,
@@ -12,12 +12,12 @@ define([
verifyErrorMessage,
ACCOUNTS_API_URL = '/api/user/v1/accounts/';
createTeamsUrl = function (teamId) {
createTeamsUrl = function(teamId) {
return TeamSpecHelpers.testContext.teamsUrl + teamId + '?expand=user';
};
createTeamModelData = function (teamId, teamName, membership) {
return {
createTeamModelData = function(teamId, teamName, membership) {
return {
id: teamId,
name: teamName,
membership: membership,
@@ -26,7 +26,7 @@ define([
};
createHeaderActionsView = function(requests, maxTeamSize, currentUsername, teamModelData, showEditButton) {
var model = new TeamModel(teamModelData, { parse: true }),
var model = new TeamModel(teamModelData, {parse: true}),
context = TeamSpecHelpers.createMockContext({
maxTeamSize: maxTeamSize,
userInfo: TeamSpecHelpers.createMockUserInfo({
@@ -46,37 +46,36 @@ define([
).render();
};
createMembershipData = function (username) {
createMembershipData = function(username) {
return [
{
"user": {
"username": username,
"url": ACCOUNTS_API_URL + username
'user': {
'username': username,
'url': ACCOUNTS_API_URL + username
}
}
];
};
describe('JoinButton', function () {
beforeEach(function () {
describe('JoinButton', function() {
beforeEach(function() {
setFixtures(
'<div class="teams-content"><div class="msg-content"><div class="copy"></div></div><div class="header-action-view"></div></div>'
);
});
verifyErrorMessage = function (requests, errorMessage, expectedMessage, joinTeam) {
verifyErrorMessage = function(requests, errorMessage, expectedMessage, joinTeam) {
var view = createHeaderActionsView(requests, 1, 'ma', createTeamModelData('teamA', 'teamAlpha', []));
if (joinTeam) {
// if we want the error to return when user try to join team, respond with no membership
AjaxHelpers.respondWithJson(requests, {"count": 0});
AjaxHelpers.respondWithJson(requests, {'count': 0});
view.$('.action.action-primary').click();
}
AjaxHelpers.respondWithTextError(requests, 400, errorMessage);
expect($('.msg-content .copy').text().trim()).toBe(expectedMessage);
};
it('can render itself', function () {
it('can render itself', function() {
var requests = AjaxHelpers.requests(this);
var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma'));
var view = createHeaderActionsView(requests, 1, 'ma', teamModelData);
@@ -84,7 +83,7 @@ define([
expect(view.$('.join-team').length).toEqual(1);
});
it('can join team successfully', function () {
it('can join team successfully', function() {
var requests = AjaxHelpers.requests(this);
var currentUsername = 'ma1';
var teamId = 'teamA';
@@ -103,7 +102,7 @@ define([
);
// current user is not a member of any team so we should see the Join Team button
AjaxHelpers.respondWithJson(requests, {"count": 0});
AjaxHelpers.respondWithJson(requests, {'count': 0});
expect(view.$('.action.action-primary').length).toEqual(1);
// a post request will be sent to add current user to current team
@@ -132,7 +131,7 @@ define([
expect(view.$('.join-team-message').length).toEqual(0);
});
it('shows already member message', function () {
it('shows already member message', function() {
var requests = AjaxHelpers.requests(this);
var currentUsername = 'ma1';
var view = createHeaderActionsView(requests, 1, currentUsername, createTeamModelData('teamA', 'teamAlpha', []));
@@ -148,12 +147,12 @@ define([
);
// current user is a member of another team so we should see the correct message
AjaxHelpers.respondWithJson(requests, {"count": 1});
AjaxHelpers.respondWithJson(requests, {'count': 1});
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').text().trim()).toBe(view.alreadyMemberMessage);
});
it('shows team full message', function () {
it('shows team full message', function() {
var requests = AjaxHelpers.requests(this);
var view = createHeaderActionsView(
requests,
@@ -171,7 +170,7 @@ define([
AjaxHelpers.expectNoRequests(requests);
});
it('shows correct error message if user fails to join team', function () {
it('shows correct error message if user fails to join team', function() {
var requests = AjaxHelpers.requests(this);
// verify user_message
@@ -199,7 +198,7 @@ define([
);
});
it('shows correct error message if initializing the view fails', function () {
it('shows correct error message if initializing the view fails', function() {
var requests = AjaxHelpers.requests(this);
// verify user_message
@@ -231,17 +230,17 @@ define([
expect(view.$('.action-edit-team').length).toEqual(showEditButton ? 1 : 0);
};
it('renders when option showEditButton is true', function () {
it('renders when option showEditButton is true', function() {
var requests = AjaxHelpers.requests(this);
createAndAssertView(requests, true);
});
it('does not render when option showEditButton is false', function () {
it('does not render when option showEditButton is false', function() {
var requests = AjaxHelpers.requests(this);
createAndAssertView(requests, false);
});
it("can navigate to correct url", function () {
it('can navigate to correct url', function() {
var requests = AjaxHelpers.requests(this);
spyOn(Backbone.history, 'navigate');
createAndAssertView(requests, true);

View File

@@ -2,9 +2,9 @@ define([
'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'teams/js/models/team',
'teams/js/views/team_profile', 'teams/js/spec_helpers/team_spec_helpers',
'xmodule_js/common_static/common/js/spec_helpers/discussion_spec_helper'
], function (_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) {
], function(_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) {
'use strict';
describe('TeamProfileView', function () {
describe('TeamProfileView', function() {
var profileView, createTeamProfileView, createTeamModelData, clickLeaveTeam,
teamModel,
leaveTeamLinkSelector = '.leave-team-link',
@@ -20,17 +20,17 @@ define([
}
];
beforeEach(function () {
beforeEach(function() {
setFixtures('<div id="page-prompt"></div>' +
'<div class="teams-content"><div class="msg-content"><div class="copy"></div></div></div>' +
'<div class="profile-view"></div>');
DiscussionSpecHelper.setUnderscoreFixtures();
});
createTeamModelData = function (options) {
createTeamModelData = function(options) {
return {
id: "test-team",
name: "Test Team",
id: 'test-team',
name: 'Test Team',
discussion_topic_id: TeamSpecHelpers.testTeamDiscussionID,
country: options.country || '',
language: options.language || '',
@@ -40,7 +40,7 @@ define([
};
createTeamProfileView = function(requests, options) {
teamModel = new TeamModel(createTeamModelData(options), { parse: true });
teamModel = new TeamModel(createTeamModelData(options), {parse: true});
profileView = new TeamProfileView({
el: $('.profile-view'),
teamEvents: TeamSpecHelpers.teamEvents,
@@ -95,13 +95,13 @@ define([
};
describe('DiscussionsView', function() {
it('can render itself', function () {
it('can render itself', function() {
var requests = AjaxHelpers.requests(this),
view = createTeamProfileView(requests, {});
expect(view.$('.discussion-thread').length).toEqual(3);
});
it('shows New Post button when user joins a team', function () {
it('shows New Post button when user joins a team', function() {
var requests = AjaxHelpers.requests(this),
view = createTeamProfileView(requests, {});
@@ -111,7 +111,7 @@ define([
expect(view.$('.new-post-btn').length).toEqual(1);
});
it('hides New Post button when user left a team', function () {
it('hides New Post button when user left a team', function() {
var requests = AjaxHelpers.requests(this),
view = createTeamProfileView(requests, {membership: DEFAULT_MEMBERSHIP});
@@ -122,7 +122,6 @@ define([
});
describe('TeamDetailsView', function() {
var assertTeamDetails = function(view, members, memberOfTeam) {
expect(view.$('.team-detail-header').text()).toBe('Team Details');
expect(view.$('.team-country').text()).toContain('United States');
@@ -133,7 +132,6 @@ define([
};
describe('Non-Member', function() {
it('can render itself', function() {
var requests = AjaxHelpers.requests(this);
var view = createTeamProfileView(requests, {
@@ -156,7 +154,6 @@ define([
});
describe('Member', function() {
it('can render itself', function() {
var requests = AjaxHelpers.requests(this);
var view = createTeamProfileView(requests, {
@@ -172,7 +169,7 @@ define([
// assert user profile page url.
expect(view.$('.member-profile').attr('href')).toBe('/u/' + TeamSpecHelpers.testUser);
//Verify that the leave team link is present
// Verify that the leave team link is present
expect(view.$(leaveTeamLinkSelector).text()).toContain('Leave Team');
});
@@ -198,10 +195,10 @@ define([
assertTeamDetails(view, 1, true);
});
it('shows correct error messages', function () {
it('shows correct error messages', function() {
var requests = AjaxHelpers.requests(this);
var verifyErrorMessage = function (requests, errorMessage, expectedMessage) {
var verifyErrorMessage = function(requests, errorMessage, expectedMessage) {
var view = createTeamProfileView(
requests, {country: 'US', language: 'en', membership: DEFAULT_MEMBERSHIP}
);

View File

@@ -3,10 +3,10 @@ define([
'teams/js/collections/team',
'teams/js/views/teams',
'teams/js/spec_helpers/team_spec_helpers'
], function (Backbone, TeamCollection, TeamsView, TeamSpecHelpers) {
], function(Backbone, TeamCollection, TeamsView, TeamSpecHelpers) {
'use strict';
describe('Teams View', function () {
beforeEach(function () {
describe('Teams View', function() {
beforeEach(function() {
setFixtures('<div class="teams-container"></div>');
});
@@ -19,7 +19,7 @@ define([
}).render();
};
it('can render itself', function () {
it('can render itself', function() {
var testTeamData = TeamSpecHelpers.createMockTeamData(1, 5),
teamsView = createTeamsView({
teams: TeamSpecHelpers.createMockTeams({

View File

@@ -57,9 +57,9 @@ define([
});
afterEach(function() {
Backbone.history.stop();
$(document).off('ajaxError', TeamsTabView.prototype.errorHandler);
}
Backbone.history.stop();
$(document).off('ajaxError', TeamsTabView.prototype.errorHandler);
}
);
describe('Navigation', function() {
@@ -71,7 +71,7 @@ define([
expect(teamsTabView.$('.breadcrumbs').length).toBe(0);
});
it('does not interfere with anchor links to #main', function () {
it('does not interfere with anchor links to #main', function() {
var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('#main', {trigger: true});
expect(teamsTabView.$('.wrapper-msg')).toHaveClass('is-hidden');
@@ -193,8 +193,8 @@ define([
describe('Discussion privileges', function() {
it('allows privileged access to any team', function() {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: true})
});
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: true})
});
// Note: using `undefined` here to ensure that we
// don't even look at the team when the user is
// privileged
@@ -203,11 +203,11 @@ define([
it('allows access to a team which an unprivileged user is a member of', function() {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({
username: TeamSpecHelpers.testUser,
privileged: false
})
});
userInfo: TeamSpecHelpers.createMockUserInfo({
username: TeamSpecHelpers.testUser,
privileged: false
})
});
expect(teamsTabView.readOnlyDiscussion({
attributes: {
membership: [{
@@ -221,8 +221,8 @@ define([
it('does not allow access if the user is neither privileged nor a team member', function() {
var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: false, staff: true})
});
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: false, staff: true})
});
expect(teamsTabView.readOnlyDiscussion({
attributes: {membership: []}
})).toBe(true);
@@ -267,7 +267,7 @@ define([
// Clear the search and submit it again
teamsTabView.$('.search-field').val('');
teamsTabView.$('.action-search').click();
verifyTeamsRequest({
order_by: 'last_activity_at'
});

View File

@@ -2,9 +2,8 @@ define(['jquery',
'underscore',
'teams/js/views/topic_card',
'teams/js/models/topic'],
function ($, _, TopicCardView, Topic) {
describe('Topic card view', function () {
function($, _, TopicCardView, Topic) {
describe('Topic card view', function() {
var createTopicCardView = function() {
return new TopicCardView({
model: new Topic({
@@ -16,11 +15,11 @@ define(['jquery',
});
};
beforeEach(function () {
beforeEach(function() {
spyOn(TopicCardView.prototype, 'action');
});
it('can render itself', function () {
it('can render itself', function() {
var view = createTopicCardView();
expect(view.$el).toHaveClass('square-card');
expect(view.$el.find('.card-title').text()).toContain('Renewable Energy');
@@ -29,7 +28,7 @@ define(['jquery',
expect(view.$el.find('.action .sr').text()).toContain('View Teams in the Renewable Energy Topic');
});
it('navigates when action button is clicked', function () {
it('navigates when action button is clicked', function() {
var view = createTopicCardView();
view.$el.find('.action').trigger('click');
// TODO test actual navigation once implemented

View File

@@ -4,9 +4,9 @@ define([
'teams/js/views/topic_teams',
'teams/js/spec_helpers/team_spec_helpers',
'common/js/spec_helpers/page_helpers'
], function (Backbone, _, TopicTeamsView, TeamSpecHelpers, PageHelpers) {
], function(Backbone, _, TopicTeamsView, TeamSpecHelpers, PageHelpers) {
'use strict';
describe('Topic Teams View', function () {
describe('Topic Teams View', function() {
var createTopicTeamsView = function(options) {
options = options || {};
var myTeamsCollection = options.myTeamsCollection || TeamSpecHelpers.createMockTeams({results: []});
@@ -38,12 +38,12 @@ define([
}
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div class="teams-container"></div>');
PageHelpers.preventBackboneChangingUrl();
});
it('can render itself', function () {
it('can render itself', function() {
var testTeamData = TeamSpecHelpers.createMockTeamData(1, 5),
teamsView = createTopicTeamsView({
teams: TeamSpecHelpers.createMockTeams({
@@ -61,21 +61,21 @@ define([
verifyActions(teamsView);
});
it('can browse all teams', function () {
it('can browse all teams', function() {
var teamsView = createTopicTeamsView();
spyOn(Backbone.history, 'navigate');
teamsView.$('.browse-teams').click();
expect(Backbone.history.navigate.calls.mostRecent().args[0]).toBe('browse');
});
it('gives the search field focus when clicking on the search teams link', function () {
it('gives the search field focus when clicking on the search teams link', function() {
var teamsView = createTopicTeamsView();
spyOn($.fn, 'focus').and.callThrough();
teamsView.$('.search-teams').click();
expect(teamsView.$('.search-field').first().focus).toHaveBeenCalled();
});
it('can show the create team modal', function () {
it('can show the create team modal', function() {
var teamsView = createTopicTeamsView();
spyOn(Backbone.history, 'navigate');
teamsView.$('a.create-team').click();
@@ -84,18 +84,18 @@ define([
);
});
it('does not show actions for a user already in a team', function () {
it('does not show actions for a user already in a team', function() {
var teamsView = createTopicTeamsView({myTeamsCollection: TeamSpecHelpers.createMockTeams()});
verifyActions(teamsView, {showActions: false});
});
it('shows actions for a privileged user already in a team', function () {
var teamsView = createTopicTeamsView({ privileged: true });
it('shows actions for a privileged user already in a team', function() {
var teamsView = createTopicTeamsView({privileged: true});
verifyActions(teamsView);
});
it('shows actions for a staff user already in a team', function () {
var teamsView = createTopicTeamsView({ privileged: false, staff: true });
it('shows actions for a staff user already in a team', function() {
var teamsView = createTopicTeamsView({privileged: false, staff: true});
verifyActions(teamsView);
});

View File

@@ -1,9 +1,9 @@
define([
'backbone', 'underscore', 'teams/js/collections/topic', 'teams/js/views/topics',
'teams/js/spec_helpers/team_spec_helpers', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
], function (Backbone, _, TopicCollection, TopicsView, TeamSpecHelpers, AjaxHelpers) {
], function(Backbone, _, TopicCollection, TopicsView, TeamSpecHelpers, AjaxHelpers) {
'use strict';
describe('TopicsView', function () {
describe('TopicsView', function() {
var initialTopics, topicCollection, createTopicsView, triggerUpdateEvent;
createTopicsView = function() {
@@ -16,25 +16,25 @@ define([
};
triggerUpdateEvent = function(topicsView, sendJoinAfter) {
topicsView.collection.teamEvents.trigger('teams:update', { action: 'create' });
topicsView.collection.teamEvents.trigger('teams:update', {action: 'create'});
if (sendJoinAfter) {
topicsView.collection.teamEvents.trigger('teams:update', { action: 'join' });
topicsView.collection.teamEvents.trigger('teams:update', {action: 'join'});
}
topicsView.render();
};
beforeEach(function () {
beforeEach(function() {
setFixtures('<div class="topics-container"></div>');
initialTopics = TeamSpecHelpers.createMockTopicData(1, 5);
topicCollection = TeamSpecHelpers.createMockTopicCollection(initialTopics);
});
it('can render the first of many pages', function () {
it('can render the first of many pages', function() {
var topicsView = createTopicsView(),
footerEl = topicsView.$('.topics-paging-footer'),
topicCards = topicsView.$('.topic-card');
expect(topicsView.$('.topics-paging-header').text()).toMatch('Showing 1-5 out of 6 total');
_.each(initialTopics, function (topic, index) {
_.each(initialTopics, function(topic, index) {
var currentCard = topicCards.eq(index);
expect(currentCard.text()).toMatch(topic.name);
expect(currentCard.text()).toMatch(topic.description);

View File

@@ -4,14 +4,14 @@ define([
'teams/js/collections/team',
'teams/js/collections/topic',
'teams/js/models/topic'
], function (Backbone, _, TeamCollection, TopicCollection, TopicModel) {
], function(Backbone, _, TeamCollection, TopicCollection, TopicModel) {
'use strict';
var createMockPostResponse, createMockDiscussionResponse, createAnnotatedContentInfo, createMockThreadResponse,
createMockTopicData, createMockTopicCollection, createMockTopic,
testCourseID = 'course/1',
testUser = 'testUser',
testTopicID = 'test-topic-1',
testTeamDiscussionID = "12345",
testTeamDiscussionID = '12345',
teamEvents = _.clone(Backbone.Events),
testCountries = [
['', ''],
@@ -26,14 +26,14 @@ define([
['fr', 'French']
];
var createMockTeamData = function (startIndex, stopIndex) {
return _.map(_.range(startIndex, stopIndex + 1), function (i) {
var id = "id" + i;
var createMockTeamData = function(startIndex, stopIndex) {
return _.map(_.range(startIndex, stopIndex + 1), function(i) {
var id = 'id' + i;
return {
name: "team " + i,
name: 'team ' + i,
id: id,
language: testLanguages[i%4][0],
country: testCountries[i%4][0],
language: testLanguages[i % 4][0],
country: testCountries[i % 4][0],
membership: [],
last_activity_at: '',
topic_id: 'topic_id' + i,
@@ -56,7 +56,7 @@ define([
};
var createMockTeams = function(responseOptions, options, collectionType) {
if(_.isUndefined(collectionType)) {
if (_.isUndefined(collectionType)) {
collectionType = TeamCollection;
}
return new collectionType(
@@ -72,7 +72,7 @@ define([
var createMockTeamMembershipsData = function(startIndex, stopIndex) {
var teams = createMockTeamData(startIndex, stopIndex);
return _.map(_.range(startIndex, stopIndex + 1), function (i) {
return _.map(_.range(startIndex, stopIndex + 1), function(i) {
return {
user: {
username: testUser,
@@ -81,7 +81,7 @@ define([
image_url_small: 'test_profile_image'
}
},
team: teams[i-1]
team: teams[i - 1]
};
});
};
@@ -100,7 +100,7 @@ define([
var verifyCards = function(view, teams) {
var teamCards = view.$('.team-card');
_.each(teams, function (team, index) {
_.each(teams, function(team, index) {
var currentCard = teamCards.eq(index);
expect(currentCard.text()).toMatch(team.name);
expect(currentCard.text()).toMatch(_.object(testLanguages)[team.language]);
@@ -108,7 +108,7 @@ define([
});
};
var triggerTeamEvent = function (action) {
var triggerTeamEvent = function(action) {
teamEvents.trigger('teams:update', {action: action});
};
@@ -119,20 +119,20 @@ define([
course_id: testCourseID,
commentable_id: testTeamDiscussionID,
type: 'thread',
body: "",
body: '',
anonymous_to_peers: false,
unread_comments_count: 0,
updated_at: '2015-07-29T18:44:56Z',
group_name: 'Default Group',
pinned: false,
votes: {count: 0, down_count: 0, point: 0, up_count: 0},
user_id: "9",
user_id: '9',
abuse_flaggers: [],
closed: false,
at_position_list: [],
read: false,
anonymous: false,
created_at: "2015-07-29T18:44:56Z",
created_at: '2015-07-29T18:44:56Z',
thread_type: 'discussion',
comments_count: 0,
group_id: 1,
@@ -145,36 +145,36 @@ define([
createMockDiscussionResponse = function(threads) {
if (_.isUndefined(threads)) {
threads = [
createMockPostResponse({ id: "1", title: "First Post"}),
createMockPostResponse({ id: "2", title: "Second Post"}),
createMockPostResponse({ id: "3", title: "Third Post"})
createMockPostResponse({id: '1', title: 'First Post'}),
createMockPostResponse({id: '2', title: 'Second Post'}),
createMockPostResponse({id: '3', title: 'Third Post'})
];
}
return {
"num_pages": 1,
"page": 1,
"discussion_data": threads,
"user_info": {
"username": testUser,
"follower_ids": [],
"default_sort_key": "date",
"downvoted_ids": [],
"subscribed_thread_ids": [],
"upvoted_ids": [],
"external_id": "9",
"id": "9",
"subscribed_user_ids": [],
"subscribed_commentable_ids": []
'num_pages': 1,
'page': 1,
'discussion_data': threads,
'user_info': {
'username': testUser,
'follower_ids': [],
'default_sort_key': 'date',
'downvoted_ids': [],
'subscribed_thread_ids': [],
'upvoted_ids': [],
'external_id': '9',
'id': '9',
'subscribed_user_ids': [],
'subscribed_commentable_ids': []
},
"annotated_content_info": {
'annotated_content_info': {
},
"roles": {"Moderator": [], "Administrator": [], "Community TA": []},
"course_settings": {
"is_cohorted": false,
"allow_anonymous_to_peers": false,
"allow_anonymous": true,
"category_map": {"subcategories": {}, "children": [], "entries": {}},
"cohorts": []
'roles': {'Moderator': [], 'Administrator': [], 'Community TA': []},
'course_settings': {
'is_cohorted': false,
'allow_anonymous_to_peers': false,
'allow_anonymous': true,
'category_map': {'subcategories': {}, 'children': [], 'entries': {}},
'cohorts': []
}
};
};
@@ -203,36 +203,36 @@ define([
comments_count: 0,
anonymous_to_peers: false,
unread_comments_count: 0,
updated_at: "2015-08-04T21:44:28Z",
updated_at: '2015-08-04T21:44:28Z',
resp_skip: 0,
id: "55c1323c56c02ce921000001",
id: '55c1323c56c02ce921000001',
pinned: false,
votes: {"count": 0, "down_count": 0, "point": 0, "up_count": 0},
votes: {'count': 0, 'down_count': 0, 'point': 0, 'up_count': 0},
resp_limit: 25,
abuse_flaggers: [],
closed: false,
resp_total: 1,
at_position_list: [],
type: "thread",
type: 'thread',
read: true,
anonymous: false,
user_id: "5",
created_at: "2015-08-04T21:44:28Z",
thread_type: "discussion",
context: "standalone",
user_id: '5',
created_at: '2015-08-04T21:44:28Z',
thread_type: 'discussion',
context: 'standalone',
endorsed: false
},
options
);
};
createMockTopicData = function (startIndex, stopIndex) {
return _.map(_.range(startIndex, stopIndex + 1), function (i) {
createMockTopicData = function(startIndex, stopIndex) {
return _.map(_.range(startIndex, stopIndex + 1), function(i) {
return {
"description": "Test description " + i,
"name": "Test Topic " + i,
"id": "test-topic-" + i,
"team_count": 0
'description': 'Test description ' + i,
'name': 'Test Topic ' + i,
'id': 'test-topic-' + i,
'team_count': 0
};
});
};
@@ -273,7 +273,7 @@ define([
return _.extend({}, testContext, options);
};
createMockTopicCollection = function (topicData) {
createMockTopicCollection = function(topicData) {
topicData = topicData !== undefined ? topicData : createMockTopicData(1, 5);
return new TopicCollection(

View File

@@ -1,12 +1,12 @@
;(function (define) {
(function(define) {
'use strict';
define(['jquery', 'teams/js/views/teams_tab'],
function ($, TeamsTabView) {
return function (options) {
function($, TeamsTabView) {
return function(options) {
var teamsTab = new TeamsTabView({
el: $('.teams-content'),
context: options,
viewLabel: gettext("Teams")
viewLabel: gettext('Teams')
});
teamsTab.start();
};

View File

@@ -2,14 +2,14 @@
* Utility methods for emitting teams events. See the event spec:
* https://openedx.atlassian.net/wiki/display/AN/Teams+Feature+Event+Design
*/
;(function (define) {
(function(define) {
'use strict';
define([
'logger'
], function (Logger) {
], function(Logger) {
var TeamAnalytics = {
emitPageViewed: function (page_name, topic_id, team_id) {
emitPageViewed: function(page_name, topic_id, team_id) {
Logger.log('edx.team.page_viewed', {
page_name: page_name,
topic_id: topic_id,

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone',
@@ -8,7 +8,7 @@
'teams/js/models/team',
'common/js/components/utils/view_utils',
'text!teams/templates/edit-team.underscore'],
function (Backbone, _, gettext, FieldViews, TeamModel, ViewUtils, editTeamTemplate) {
function(Backbone, _, gettext, FieldViews, TeamModel, ViewUtils, editTeamTemplate) {
return Backbone.View.extend({
maxTeamNameLength: 255,
@@ -30,12 +30,12 @@
if (this.action === 'create') {
this.teamModel = new TeamModel({});
this.teamModel.url = this.context.teamsUrl;
this.primaryButtonTitle = gettext("Create");
} else if(this.action === 'edit' ) {
this.primaryButtonTitle = gettext('Create');
} else if (this.action === 'edit') {
this.teamModel = options.model;
this.teamModel.url = this.context.teamsDetailUrl.replace('team_id', options.model.get('id')) +
'?expand=user';
this.primaryButtonTitle = gettext("Update");
this.primaryButtonTitle = gettext('Update');
}
this.teamNameField = new FieldViews.TextFieldView({
@@ -78,7 +78,7 @@
},
render: function() {
this.$el.html(_.template(editTeamTemplate) ({
this.$el.html(_.template(editTeamTemplate)({
primaryButtonTitle: this.primaryButtonTitle,
action: this.action,
totalMembers: _.isUndefined(this.teamModel) ? 0 : this.teamModel.get('membership').length
@@ -99,7 +99,7 @@
}
},
createOrUpdateTeam: function (event) {
createOrUpdateTeam: function(event) {
event.preventDefault();
var view = this,
teamLanguage = this.teamLanguageField.fieldValue(),
@@ -117,7 +117,7 @@
if (this.action === 'create') {
data.course_id = this.context.courseID;
data.topic_id = this.topic.id;
} else if (this.action === 'edit' ) {
} else if (this.action === 'edit') {
saveOptions.patch = true;
saveOptions.contentType = 'application/merge-patch+json';
}
@@ -140,15 +140,15 @@
})
.fail(function(data) {
var response = JSON.parse(data.responseText);
var message = gettext("An error occurred. Please try again.");
if ('user_message' in response){
var message = gettext('An error occurred. Please try again.');
if ('user_message' in response) {
message = response.user_message;
}
view.showMessage(message, message);
});
},
validateTeamData: function (data) {
validateTeamData: function(data) {
var status = true,
message = gettext('Check the highlighted fields below and try again.');
var srMessages = [];
@@ -156,7 +156,7 @@
this.teamNameField.unhighlightField();
this.teamDescriptionField.unhighlightField();
if (_.isEmpty(data.name.trim()) ) {
if (_.isEmpty(data.name.trim())) {
status = false;
this.teamNameField.highlightFieldOnError();
srMessages.push(
@@ -170,7 +170,7 @@
);
}
if (_.isEmpty(data.description.trim()) ) {
if (_.isEmpty(data.description.trim())) {
status = false;
this.teamDescriptionField.highlightFieldOnError();
srMessages.push(
@@ -191,7 +191,7 @@
};
},
showMessage: function (message, screenReaderMessage) {
showMessage: function(message, screenReaderMessage) {
this.$('.wrapper-msg').removeClass('is-hidden');
this.$('.msg-content .copy p').text(message);
this.$('.wrapper-msg').focus();
@@ -201,12 +201,12 @@
}
},
cancelAndGoBack: function (event) {
cancelAndGoBack: function(event) {
event.preventDefault();
var url;
if (this.action === 'create') {
url = 'topics/' + this.topic.id;
} else if (this.action === 'edit' ) {
} else if (this.action === 'edit') {
url = 'teams/' + this.topic.id + '/' + this.teamModel.get('id');
}
Backbone.history.navigate(url, {trigger: true});

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone',
@@ -11,11 +11,11 @@
'text!teams/templates/edit-team-member.underscore',
'text!teams/templates/date.underscore'
],
function (Backbone, $, _, gettext, TeamModel, TeamUtils, ViewUtils, editTeamMemberTemplate, dateTemplate) {
function(Backbone, $, _, gettext, TeamModel, TeamUtils, ViewUtils, editTeamMemberTemplate, dateTemplate) {
return Backbone.View.extend({
dateTemplate: _.template(dateTemplate),
teamMemberTemplate: _.template(editTeamMemberTemplate),
errorMessage: gettext("An error occurred while removing the member from the team. Try again."),
errorMessage: gettext('An error occurred while removing the member from the team. Try again.'),
events: {
'click .action-remove-member': 'removeMember'
@@ -28,7 +28,7 @@
// as the actual user to be removed from the team will be added on before calling DELETE.
this.teamMembershipDetailUrl = options.context.teamMembershipDetailUrl.substring(
0, this.options.context.teamMembershipDetailUrl.lastIndexOf('team_id')
) + this.model.get('id') + ",";
) + this.model.get('id') + ',';
this.teamEvents = options.teamEvents;
},
@@ -50,14 +50,14 @@
_.each(this.model.get('membership'), function(membership) {
dateJoined = interpolate(
// Translators: 'date' is a placeholder for a fuzzy, relative timestamp (see: https://github.com/rmm5t/jquery-timeago)
gettext("Joined %(date)s"),
gettext('Joined %(date)s'),
{date: self.dateTemplate({date: membership.date_joined})},
true
);
lastActivity = interpolate(
// Translators: 'date' is a placeholder for a fuzzy, relative timestamp (see: https://github.com/rmm5t/jquery-timeago)
gettext("Last Activity %(date)s"),
gettext('Last Activity %(date)s'),
{date: self.dateTemplate({date: membership.last_activity_at})},
true
);
@@ -74,7 +74,7 @@
this.$('abbr').timeago();
},
removeMember: function (event) {
removeMember: function(event) {
var self = this, username = $(event.currentTarget).data('username');
event.preventDefault();
@@ -82,17 +82,17 @@
gettext('Remove this team member?'),
gettext('This learner will be removed from the team, allowing another learner to take the available spot.'),
gettext('Remove'),
function () {
function() {
$.ajax({
type: 'DELETE',
url: self.teamMembershipDetailUrl.concat(username, '?admin=true')
}).done(function () {
}).done(function() {
self.teamEvents.trigger('teams:update', {
action: 'leave',
team: self.model
});
self.model.fetch().done(function() { self.render(); });
}).fail(function (data) {
}).fail(function(data) {
TeamUtils.parseAndShowMessage(data, self.errorMessage);
});
}

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone',
@@ -8,7 +8,7 @@
'teams/js/views/team_utils',
'common/js/components/utils/view_utils',
'text!teams/templates/instructor-tools.underscore'],
function (Backbone, _, gettext, StringUtils, TeamUtils, ViewUtils, instructorToolbarTemplate) {
function(Backbone, _, gettext, StringUtils, TeamUtils, ViewUtils, instructorToolbarTemplate) {
return Backbone.View.extend({
events: {
@@ -27,7 +27,7 @@
return this;
},
deleteTeam: function (event) {
deleteTeam: function(event) {
event.preventDefault();
ViewUtils.confirmThenRunOperation(
gettext('Delete this team?'),
@@ -37,17 +37,17 @@
);
},
editMembership: function (event) {
editMembership: function(event) {
event.preventDefault();
Backbone.history.navigate(
'teams/' + this.team.get('topic_id') + '/' + this.team.id +'/edit-team/manage-members',
'teams/' + this.team.get('topic_id') + '/' + this.team.id + '/edit-team/manage-members',
{trigger: true}
);
},
handleDelete: function () {
handleDelete: function() {
var self = this,
postDelete = function () {
postDelete = function() {
self.teamEvents.trigger('teams:update', {
action: 'delete',
team: self.team
@@ -62,7 +62,7 @@
'success'
);
};
this.team.destroy().then(postDelete).fail(function (response) {
this.team.destroy().then(postDelete).fail(function(response) {
// In the 404 case, this team has already been
// deleted by someone else. Since the team was
// successfully deleted anyway, just show a

View File

@@ -1,8 +1,8 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'gettext', 'teams/js/views/teams'],
function (Backbone, gettext, TeamsView) {
function(Backbone, gettext, TeamsView) {
var MyTeamsView = TeamsView.extend({
render: function() {
var view = this;

View File

@@ -1,4 +1,4 @@
(function (define) {
(function(define) {
'use strict';
define([
'jquery',
@@ -11,7 +11,7 @@
'text!teams/templates/team-membership-details.underscore',
'text!teams/templates/team-country-language.underscore',
'text!teams/templates/date.underscore'
], function (
], function(
$,
Backbone,
_,
@@ -30,15 +30,15 @@
className: 'team-members',
template: _.template(teamMembershipDetailsTemplate),
initialize: function (options) {
initialize: function(options) {
this.maxTeamSize = options.maxTeamSize;
this.memberships = options.memberships;
},
render: function () {
var allMemberships = _(this.memberships).sortBy(function (member) {
return new Date(member.last_activity_at);
}).reverse(),
render: function() {
var allMemberships = _(this.memberships).sortBy(function(member) {
return new Date(member.last_activity_at);
}).reverse(),
displayableMemberships = allMemberships.slice(0, 5),
maxMemberCount = this.maxTeamSize;
this.$el.html(this.template({
@@ -55,7 +55,7 @@
TeamCountryLanguageView = Backbone.View.extend({
template: _.template(teamCountryLanguageTemplate),
initialize: function (options) {
initialize: function(options) {
this.countries = options.countries;
this.languages = options.languages;
},
@@ -74,18 +74,18 @@
className: 'team-activity',
template: _.template(dateTemplate),
initialize: function (options) {
initialize: function(options) {
this.date = options.date;
},
render: function () {
render: function() {
var lastActivity = moment(this.date),
currentLanguage = $('html').attr('lang');
lastActivity.locale(currentLanguage);
this.$el.html(
interpolate(
// Translators: 'date' is a placeholder for a fuzzy, relative timestamp (see: http://momentjs.com/)
gettext("Last activity %(date)s"),
gettext('Last activity %(date)s'),
{date: this.template({date: lastActivity.format('MMMM Do YYYY, h:mm:ss a')})},
true
)
@@ -95,7 +95,7 @@
});
TeamCardView = CardView.extend({
initialize: function () {
initialize: function() {
CardView.prototype.initialize.apply(this, arguments);
// TODO: show last activity detail view
this.detailViews = [
@@ -107,16 +107,16 @@
}),
new TeamActivityView({date: this.model.get('last_activity_at')})
];
this.model.on('change:membership', function () {
this.model.on('change:membership', function() {
this.detailViews[0].memberships = this.model.get('membership');
}, this);
},
configuration: 'list_card',
cardClass: 'team-card',
title: function () { return this.model.get('name'); },
description: function () { return this.model.get('description'); },
details: function () { return this.detailViews; },
title: function() { return this.model.get('name'); },
description: function() { return this.model.get('description'); },
details: function() { return this.detailViews; },
actionClass: 'action-view',
actionContent: function() {
return interpolate(
@@ -125,7 +125,7 @@
true
);
},
actionUrl: function () {
actionUrl: function() {
return '#teams/' + this.model.get('topic_id') + '/' + this.model.get('id');
}
});

View File

@@ -1,16 +1,16 @@
/**
* View that shows the discussion for a team.
*/
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'underscore', 'gettext', 'DiscussionModuleView'],
function (Backbone, _, gettext, DiscussionModuleView) {
function(Backbone, _, gettext, DiscussionModuleView) {
var TeamDiscussionView = Backbone.View.extend({
initialize: function () {
window.$$course_id = this.$el.data("course-id");
initialize: function() {
window.$$course_id = this.$el.data('course-id');
},
render: function () {
render: function() {
var discussionModuleView = new DiscussionModuleView({
el: this.$el,
context: 'standalone'

View File

@@ -1,30 +1,30 @@
/**
* View for an individual team.
*/
;(function (define) {
(function(define) {
'use strict';
define([
'backbone',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'teams/js/views/team_discussion',
'common/js/components/utils/view_utils',
'teams/js/views/team_utils',
'text!teams/templates/team-profile.underscore',
'text!teams/templates/team-member.underscore'
],
function (Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils,
'backbone',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'teams/js/views/team_discussion',
'common/js/components/utils/view_utils',
'teams/js/views/team_utils',
'text!teams/templates/team-profile.underscore',
'text!teams/templates/team-member.underscore'
],
function(Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils,
teamTemplate, teamMemberTemplate) {
var TeamProfileView = Backbone.View.extend({
errorMessage: gettext("An error occurred. Try again."),
errorMessage: gettext('An error occurred. Try again.'),
events: {
'click .leave-team-link': 'leaveTeam'
},
initialize: function (options) {
initialize: function(options) {
this.teamEvents = options.teamEvents;
this.context = options.context;
this.setFocusToHeaderFunc = options.setFocusToHeaderFunc;
@@ -32,10 +32,10 @@
this.countries = TeamUtils.selectorOptionsArrayToHashWithBlank(this.context.countries);
this.languages = TeamUtils.selectorOptionsArrayToHashWithBlank(this.context.languages);
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, 'change', this.render);
},
render: function () {
render: function() {
var memberships = this.model.get('membership'),
discussionTopicID = this.model.get('discussion_topic_id'),
isMember = TeamUtils.isUserMemberOfTeam(memberships, this.context.userInfo.username);
@@ -84,18 +84,18 @@
$(event.currentTarget).select();
},
leaveTeam: function (event) {
leaveTeam: function(event) {
event.preventDefault();
var view = this;
ViewUtils.confirmThenRunOperation(
gettext("Leave this team?"),
gettext('Leave this team?'),
gettext("If you leave, you can no longer post in this team's discussions. Your place will be available to another learner."),
gettext("Confirm"),
gettext('Confirm'),
function() {
$.ajax({
type: 'DELETE',
url: view.context.teamMembershipDetailUrl.replace('team_id', view.model.get('id'))
}).done(function (data) {
}).done(function(data) {
view.model.fetch()
.done(function() {
view.teamEvents.trigger('teams:update', {
@@ -103,7 +103,7 @@
team: view.model
});
});
}).fail(function (data) {
}).fail(function(data) {
TeamUtils.parseAndShowMessage(data, view.errorMessage);
});
}

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone',
@@ -7,16 +7,16 @@
'gettext',
'teams/js/views/team_utils',
'text!teams/templates/team-profile-header-actions.underscore'],
function (Backbone, $, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) {
function(Backbone, $, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) {
return Backbone.View.extend({
errorMessage: gettext("An error occurred. Try again."),
alreadyMemberMessage: gettext("You already belong to another team."),
teamFullMessage: gettext("This team is full."),
errorMessage: gettext('An error occurred. Try again.'),
alreadyMemberMessage: gettext('You already belong to another team.'),
teamFullMessage: gettext('This team is full.'),
events: {
"click .action-primary": "joinTeam",
"click .action-edit-team": "editTeam"
'click .action-primary': 'joinTeam',
'click .action-edit-team': 'editTeam'
},
initialize: function(options) {
@@ -25,7 +25,7 @@
this.context = options.context;
this.showEditButton = options.showEditButton;
this.topic = options.topic;
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, 'change', this.render);
},
render: function() {
@@ -34,7 +34,7 @@
message,
showJoinButton,
teamHasSpace;
this.getUserTeamInfo(username, this.context.maxTeamSize).done(function (info) {
this.getUserTeamInfo(username, this.context.maxTeamSize).done(function(info) {
teamHasSpace = info.teamHasSpace;
// if user is the member of current team then we wouldn't show anything
@@ -57,7 +57,7 @@
return view;
},
joinTeam: function (event) {
joinTeam: function(event) {
var view = this;
event.preventDefault();
@@ -65,7 +65,7 @@
type: 'POST',
url: view.context.teamMembershipsUrl,
data: {'username': view.context.userInfo.username, 'team_id': view.model.get('id')}
}).done(function (data) {
}).done(function(data) {
view.model.fetch()
.done(function() {
view.teamEvents.trigger('teams:update', {
@@ -73,12 +73,12 @@
team: view.model
});
});
}).fail(function (data) {
}).fail(function(data) {
TeamUtils.parseAndShowMessage(data, view.errorMessage);
});
},
getUserTeamInfo: function (username, maxTeamSize) {
getUserTeamInfo: function(username, maxTeamSize) {
var deferred = $.Deferred();
var info = {
alreadyMember: false,
@@ -100,12 +100,12 @@
type: 'GET',
url: view.context.teamMembershipsUrl,
data: {'username': username, 'course_id': view.context.courseID}
}).done(function (data) {
}).done(function(data) {
info.alreadyMember = (data.count > 0);
info.memberOfCurrentTeam = false;
info.teamHasSpace = teamHasSpace;
deferred.resolve(info);
}).fail(function (data) {
}).fail(function(data) {
TeamUtils.parseAndShowMessage(data, view.errorMessage);
deferred.reject();
});
@@ -117,10 +117,10 @@
return deferred.promise();
},
editTeam: function (event) {
editTeam: function(event) {
event.preventDefault();
Backbone.history.navigate(
'teams/' + this.topic.id + '/' + this.model.get('id') +'/edit-team',
'teams/' + this.topic.id + '/' + this.model.get('id') + '/edit-team',
{trigger: true}
);
}

View File

@@ -1,8 +1,8 @@
/* Team utility methods*/
;(function (define) {
(function(define) {
'use strict';
define(["jquery", "underscore"
], function ($, _) {
define(['jquery', 'underscore'
], function($, _) {
return {
/**
@@ -13,13 +13,13 @@
* @example selectorOptionsArrayToHashWithBlank([["a", "alpha"],["b","beta"]])
* // returns {"a":"alpha", "b":"beta", "":""}
*/
selectorOptionsArrayToHashWithBlank: function (options) {
selectorOptionsArrayToHashWithBlank: function(options) {
var map = _.object(options);
map[""] = "";
map[''] = '';
return map;
},
teamCapacityText: function (memberCount, maxMemberCount) {
teamCapacityText: function(memberCount, maxMemberCount) {
return interpolate(
// Translators: The following message displays the number of members on a team.
ngettext(
@@ -40,11 +40,11 @@
);
},
hideMessage: function () {
hideMessage: function() {
$('#teams-message').addClass('.is-hidden');
},
showMessage: function (message, type) {
showMessage: function(message, type) {
var messageElement = $('#teams-message');
if (_.isUndefined(type)) {
type = 'warning';
@@ -57,17 +57,16 @@
/**
* Parse `data` and show user message. If parsing fails than show `genericErrorMessage`
*/
parseAndShowMessage: function (data, genericErrorMessage, type) {
parseAndShowMessage: function(data, genericErrorMessage, type) {
try {
var errors = JSON.parse(data.responseText);
this.showMessage(
var errors = JSON.parse(data.responseText);
this.showMessage(
_.isUndefined(errors.user_message) ? genericErrorMessage : errors.user_message, type
);
} catch (error) {
this.showMessage(genericErrorMessage, type);
this.showMessage(genericErrorMessage, type);
}
}
};
});
}).call(this, define || RequireJS.define);

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define([
'backbone',
@@ -6,7 +6,7 @@
'teams/js/views/team_card',
'common/js/components/views/paginated_view',
'teams/js/views/team_utils'
], function (Backbone, gettext, TeamCardView, PaginatedView, TeamUtils) {
], function(Backbone, gettext, TeamCardView, PaginatedView, TeamUtils) {
var TeamsView = PaginatedView.extend({
type: 'teams',
@@ -17,7 +17,7 @@
paginationLabel: gettext('Teams Pagination'),
initialize: function (options) {
initialize: function(options) {
this.context = options.context;
this.itemViewClass = TeamCardView.extend({
router: options.router,

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone',
@@ -27,25 +27,25 @@
'teams/js/views/team_utils',
'teams/js/views/instructor_tools',
'text!teams/templates/teams_tab.underscore'],
function (Backbone, $, _, gettext, HtmlUtils, StringUtils, SearchFieldView, HeaderView, HeaderModel,
function(Backbone, $, _, gettext, HtmlUtils, StringUtils, SearchFieldView, HeaderView, HeaderModel,
TopicModel, TopicCollection, TeamModel, TeamCollection, MyTeamsCollection, TeamAnalytics,
TeamsTabbedView, TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView,
TeamMembersEditView, TeamProfileHeaderActionsView, TeamUtils, InstructorToolsView, teamsTemplate) {
var TeamsHeaderModel = HeaderModel.extend({
initialize: function () {
initialize: function() {
_.extend(this.defaults, {nav_aria_label: gettext('Topics')});
HeaderModel.prototype.initialize.call(this);
}
});
var ViewWithHeader = Backbone.View.extend({
initialize: function (options) {
initialize: function(options) {
this.header = options.header;
this.main = options.main;
this.instructorTools = options.instructorTools;
},
render: function () {
render: function() {
HtmlUtils.setHtml(this.$el, HtmlUtils.template(teamsTemplate)({}));
this.$('p.error').hide();
this.header.setElement(this.$('.teams-header')).render();
@@ -68,7 +68,7 @@
router = this.router = new Backbone.Router();
_.each([
[':default', _.bind(this.routeNotFound, this)],
['main', _.bind(function () {
['main', _.bind(function() {
// The backbone router unfortunately usurps the
// default behavior of in-page-links. This hack
// prevents the screen reader in-page-link from
@@ -80,7 +80,7 @@
['teams/:topic_id/:team_id(/)', _.bind(this.browseTeam, this)],
[new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)],
[new RegExp('^(my-teams)\/?$'), _.bind(this.goToTab, this)]
], function (route) {
], function(route) {
router.route.apply(router, route);
});
@@ -90,7 +90,7 @@
['teams/:topic_id/:team_id/edit-team/manage-members(/)',
_.bind(this.editTeamMembers, this)
]
], function (route) {
], function(route) {
router.route.apply(router, route);
});
}
@@ -132,8 +132,8 @@
});
this.mainView = this.tabbedView = this.createViewWithHeader({
title: gettext("Teams"),
description: gettext("See all teams in your course, organized by topic. Join a team to collaborate with other learners who are interested in the same topic as you are."),
title: gettext('Teams'),
description: gettext('See all teams in your course, organized by topic. Join a team to collaborate with other learners who are interested in the same topic as you are.'),
mainView: new TeamsTabbedView({
tabs: [{
title: gettext('My Team'),
@@ -172,13 +172,13 @@
errorHandler: function(event, xhr) {
if (xhr.status === 401) {
TeamUtils.showMessage(gettext(
"Your request could not be completed. Reload the page and try again."
'Your request could not be completed. Reload the page and try again.'
));
}
else if (xhr.status === 500) {
TeamUtils.showMessage(gettext(
"Your request could not be completed due to a server problem. Reload the page" +
" and try again. If the issue persists, click the Help tab to report the problem."
'Your request could not be completed due to a server problem. Reload the page' +
' and try again. If the issue persists, click the Help tab to report the problem.'
));
}
},
@@ -192,9 +192,9 @@
/**
* Render the list of teams for the given topic ID.
*/
browseTopic: function (topicID) {
browseTopic: function(topicID) {
var self = this;
this.getTeamsView(topicID).done(function (teamsView) {
this.getTeamsView(topicID).done(function(teamsView) {
self.teamsView = self.mainView = teamsView;
self.render();
TeamAnalytics.emitPageViewed('single-topic', topicID, null);
@@ -204,12 +204,12 @@
/**
* Show the search results for a team.
*/
searchTeams: function (topicID) {
searchTeams: function(topicID) {
var view = this;
if (!this.teamsCollection) {
this.router.navigate('topics/' + topicID, {trigger: true});
} else {
this.getTopic(topicID).done(function (topic) {
this.getTopic(topicID).done(function(topic) {
view.mainView = view.createTeamsListView({
topic: topic,
collection: view.teamsCollection,
@@ -230,12 +230,12 @@
/**
* Render the create new team form.
*/
newTeam: function (topicID) {
newTeam: function(topicID) {
var view = this;
this.getTopic(topicID).done(function (topic) {
this.getTopic(topicID).done(function(topic) {
view.mainView = view.createViewWithHeader({
topic: topic,
title: gettext("Create a New Team"),
title: gettext('Create a New Team'),
description: gettext("Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know."),
breadcrumbs: view.createBreadcrumbs(topic),
mainView: new TeamEditView({
@@ -253,7 +253,7 @@
/**
* Render the edit team form.
*/
editTeam: function (topicID, teamID) {
editTeam: function(topicID, teamID) {
var self = this,
editViewWithHeader;
$.when(this.getTopic(topicID), this.getTeam(teamID, false)).done(function(topic, team) {
@@ -269,8 +269,8 @@
teamEvents: self.teamEvents
});
editViewWithHeader = self.createViewWithHeader({
title: gettext("Edit Team"),
description: gettext("If you make significant changes, make sure you notify members of the team before making these changes."),
title: gettext('Edit Team'),
description: gettext('If you make significant changes, make sure you notify members of the team before making these changes.'),
breadcrumbs: self.createBreadcrumbs(topic, team),
mainView: view,
topic: topic,
@@ -287,7 +287,7 @@
*
* The backbone router entry for editing team members, using topic and team IDs.
*/
editTeamMembers: function (topicID, teamID) {
editTeamMembers: function(topicID, teamID) {
var self = this;
$.when(this.getTopic(topicID), this.getTeam(teamID, true)).done(function(topic, team) {
var view = new TeamMembersEditView({
@@ -296,13 +296,13 @@
model: team
});
self.mainView = self.createViewWithHeader({
mainView: view,
breadcrumbs: self.createBreadcrumbs(topic, team),
title: gettext("Membership"),
description: gettext("You can remove members from this team, especially if they have not participated in the team's activity."),
topic: topic,
team: team
}
mainView: view,
breadcrumbs: self.createBreadcrumbs(topic, team),
title: gettext('Membership'),
description: gettext("You can remove members from this team, especially if they have not participated in the team's activity."),
topic: topic,
team: team
}
);
self.render();
TeamAnalytics.emitPageViewed('edit-team-members', topicID, teamID);
@@ -312,7 +312,7 @@
/**
* Return a promise for the TeamsView for the given topic ID.
*/
getTeamsView: function (topicID) {
getTeamsView: function(topicID) {
// Lazily load the teams-for-topic view in
// order to avoid making an extra AJAX call.
var view = this,
@@ -331,7 +331,7 @@
perPage: 10
});
view.teamsCollection = collection;
collection.getPage(1).then(function () {
collection.getPage(1).then(function() {
var teamsView = view.createTeamsListView({
topic: topic,
collection: collection,
@@ -394,9 +394,9 @@
/**
* Browse to the team with the specified team ID belonging to the specified topic.
*/
browseTeam: function (topicID, teamID) {
browseTeam: function(topicID, teamID) {
var self = this;
this.getBrowseTeamView(topicID, teamID).done(function (browseTeamView) {
this.getBrowseTeamView(topicID, teamID).done(function(browseTeamView) {
self.mainView = browseTeamView;
self.render();
TeamAnalytics.emitPageViewed('single-team', topicID, teamID);
@@ -413,7 +413,7 @@
/**
* Return a promise for the team view for the given team ID.
*/
getBrowseTeamView: function (topicID, teamID) {
getBrowseTeamView: function(topicID, teamID) {
var self = this,
deferred = $.Deferred();
@@ -448,7 +448,7 @@
return deferred.promise();
},
canEditTeam: function () {
canEditTeam: function() {
return this.context.userInfo.privileged || this.context.userInfo.staff;
},
@@ -491,7 +491,7 @@
model: this.createHeaderModel(options),
headerActionsView: options.headerActionsView,
events: {
'click nav.breadcrumbs a.nav-item': function (event) {
'click nav.breadcrumbs a.nav-item': function(event) {
var url = $(event.currentTarget).attr('href');
event.preventDefault();
router.navigate(url, {trigger: true});
@@ -510,7 +510,7 @@
* @param topicID the string identifier for the requested topic
* @returns a jQuery deferred promise for the topic.
*/
getTopic: function (topicID) {
getTopic: function(topicID) {
// Try finding topic in the current page of the
// topicCollection. Otherwise call the topic endpoint.
var topic = this.topicsCollection.findWhere({'id': topicID}),
@@ -543,11 +543,11 @@
* @param expandUser bool to add the users info.
* @returns {promise} a jQuery deferred promise for the team.
*/
getTeam: function (teamID, expandUser) {
getTeam: function(teamID, expandUser) {
var team = this.teamsCollection ? this.teamsCollection.get(teamID) : null,
self = this,
deferred = $.Deferred(),
teamUrl = this.context.teamsUrl + teamID + (expandUser ? '?expand=user': '');
teamUrl = this.context.teamsUrl + teamID + (expandUser ? '?expand=user' : '');
if (team) {
team.url = teamUrl;
deferred.resolve(team);
@@ -582,7 +582,7 @@
// Error handling
routeNotFound: function (route) {
routeNotFound: function(route) {
this.notFoundError(
StringUtils.interpolate(
gettext('The page "{route}" could not be found.'),
@@ -591,7 +591,7 @@
);
},
topicNotFound: function (topicID) {
topicNotFound: function(topicID) {
this.notFoundError(
StringUtils.interpolate(
gettext('The topic "{topic}" could not be found.'),
@@ -600,7 +600,7 @@
);
},
teamNotFound: function (teamID) {
teamNotFound: function(teamID) {
this.notFoundError(
StringUtils.interpolate(
gettext('The team "{team}" could not be found.'),
@@ -614,7 +614,7 @@
* route that doesn't exist. "Redirects" back to
* the main teams tab, and adds an error message.
*/
notFoundError: function (message) {
notFoundError: function(message) {
this.router.navigate('my-teams', {trigger: true});
TeamUtils.showMessage(message);
},
@@ -626,11 +626,11 @@
* moderator, or administrator), or if the user
* belongs to the team.
*/
readOnlyDiscussion: function (team) {
readOnlyDiscussion: function(team) {
var userInfo = this.context.userInfo;
return !(
userInfo.privileged ||
_.any(team.attributes.membership, function (membership) {
_.any(team.attributes.membership, function(membership) {
return membership.user.username === userInfo.username;
})
);

View File

@@ -1,19 +1,19 @@
/**
* A custom TabbedView for Teams.
*/
;(function (define) {
(function(define) {
'use strict';
define([
'common/js/components/views/tabbed_view',
'teams/js/utils/team_analytics'
], function (TabbedView, TeamAnalytics) {
], function(TabbedView, TeamAnalytics) {
var TeamsTabbedView = TabbedView.extend({
/**
* Overrides TabbedView.prototype.setActiveTab in order to
* log page viewed events.
*/
setActiveTab: function (index) {
setActiveTab: function(index) {
TabbedView.prototype.setActiveTab.call(this, index);
TeamAnalytics.emitPageViewed(this.getTabMeta(index).tab.url, null, null);
}

View File

@@ -1,19 +1,19 @@
/**
* View for a topic card. Displays a Topic.
*/
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'underscore', 'gettext', 'js/components/card/views/card'],
function (Backbone, _, gettext, CardView) {
function(Backbone, _, gettext, CardView) {
var TeamCountDetailView = Backbone.View.extend({
tagName: 'p',
className: 'team-count',
initialize: function () {
initialize: function() {
this.render();
},
render: function () {
render: function() {
var team_count = this.model.get('team_count');
this.$el.html(_.escape(interpolate(
ngettext('%(team_count)s Team', '%(team_count)s Teams', team_count),
@@ -25,26 +25,26 @@
});
var TopicCardView = CardView.extend({
initialize: function () {
this.detailViews = [new TeamCountDetailView({ model: this.model })];
initialize: function() {
this.detailViews = [new TeamCountDetailView({model: this.model})];
CardView.prototype.initialize.apply(this, arguments);
},
actionUrl: function () {
actionUrl: function() {
return '#topics/' + this.model.get('id');
},
configuration: 'square_card',
cardClass: 'topic-card',
pennant: gettext('Topic'),
title: function () { return this.model.get('name'); },
description: function () { return this.model.get('description'); },
details: function () { return this.detailViews; },
title: function() { return this.model.get('name'); },
description: function() { return this.model.get('description'); },
details: function() { return this.detailViews; },
actionClass: 'action-view',
actionContent: function () {
actionContent: function() {
var screenReaderText = _.escape(interpolate(
gettext('View Teams in the %(topic_name)s Topic'),
{ topic_name: this.model.get('name') }, true
{topic_name: this.model.get('name')}, true
));
return '<span class="sr">' + screenReaderText + '</span><span class="icon fa fa-arrow-right" aria-hidden="true"></span>'; // eslint-disable-line max-len
}

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define([
'backbone',
@@ -6,36 +6,36 @@
'teams/js/views/teams',
'common/js/components/views/paging_header',
'text!teams/templates/team-actions.underscore'
], function (Backbone, gettext, TeamsView, PagingHeader, teamActionsTemplate) {
var TopicTeamsView = TeamsView.extend({
events: {
'click a.browse-teams': 'browseTeams',
'click a.search-teams': 'searchTeams',
'click a.create-team': 'showCreateTeamForm'
},
], function(Backbone, gettext, TeamsView, PagingHeader, teamActionsTemplate) {
var TopicTeamsView = TeamsView.extend({
events: {
'click a.browse-teams': 'browseTeams',
'click a.search-teams': 'searchTeams',
'click a.create-team': 'showCreateTeamForm'
},
initialize: function(options) {
this.options = _.extend({}, options);
this.showSortControls = options.showSortControls;
this.context = options.context;
this.myTeamsCollection = options.myTeamsCollection;
TeamsView.prototype.initialize.call(this, options);
},
initialize: function(options) {
this.options = _.extend({}, options);
this.showSortControls = options.showSortControls;
this.context = options.context;
this.myTeamsCollection = options.myTeamsCollection;
TeamsView.prototype.initialize.call(this, options);
},
canUserCreateTeam: function () {
canUserCreateTeam: function() {
// Note: non-staff and non-privileged users are automatically added to any team
// that they create. This means that if multiple team membership is
// disabled that they cannot create a new team when they already
// belong to one.
return this.context.staff || this.context.privileged || this.myTeamsCollection.length === 0;
},
return this.context.staff || this.context.privileged || this.myTeamsCollection.length === 0;
},
render: function() {
var self = this;
this.collection.refresh().done(function() {
TeamsView.prototype.render.call(self);
if (self.canUserCreateTeam()) {
var message = interpolate_text(
render: function() {
var self = this;
this.collection.refresh().done(function() {
TeamsView.prototype.render.call(self);
if (self.canUserCreateTeam()) {
var message = interpolate_text(
// Translators: this string is shown at the bottom of the teams page
// to find a team to join or else to create a new one. There are three
// links that need to be included in the message:
@@ -45,51 +45,51 @@
// Be careful to start each link with the appropriate start indicator
// (e.g. {browse_span_start} for #1) and finish it with {span_end}.
_.escape(gettext("{browse_span_start}Browse teams in other topics{span_end} or {search_span_start}search teams{span_end} in this topic. If you still can't find a team to join, {create_span_start}create a new team in this topic{span_end}.")),
{
'browse_span_start': '<a class="browse-teams" href="">',
'search_span_start': '<a class="search-teams" href="">',
'create_span_start': '<a class="create-team" href="">',
'span_end': '</a>'
}
{
'browse_span_start': '<a class="browse-teams" href="">',
'search_span_start': '<a class="search-teams" href="">',
'create_span_start': '<a class="create-team" href="">',
'span_end': '</a>'
}
);
self.$el.append(_.template(teamActionsTemplate)({message: message}));
}
});
return this;
},
self.$el.append(_.template(teamActionsTemplate)({message: message}));
}
});
return this;
},
browseTeams: function (event) {
event.preventDefault();
Backbone.history.navigate('browse', {trigger: true});
},
browseTeams: function(event) {
event.preventDefault();
Backbone.history.navigate('browse', {trigger: true});
},
searchTeams: function (event) {
var searchField = $('.page-header-search .search-field');
event.preventDefault();
searchField.focus();
searchField.select();
$('html, body').animate({
scrollTop: 0
}, 500);
},
searchTeams: function(event) {
var searchField = $('.page-header-search .search-field');
event.preventDefault();
searchField.focus();
searchField.select();
$('html, body').animate({
scrollTop: 0
}, 500);
},
showCreateTeamForm: function (event) {
event.preventDefault();
Backbone.history.navigate(
showCreateTeamForm: function(event) {
event.preventDefault();
Backbone.history.navigate(
'topics/' + this.model.id + '/create-team',
{trigger: true}
);
},
},
createHeaderView: function () {
return new PagingHeader({
collection: this.options.collection,
srInfo: this.srInfo,
showSortControls: this.showSortControls
});
}
});
return TopicTeamsView;
createHeaderView: function() {
return new PagingHeader({
collection: this.options.collection,
srInfo: this.srInfo,
showSortControls: this.showSortControls
});
}
});
return TopicTeamsView;
});
}).call(this, define || RequireJS.define);

View File

@@ -1,4 +1,4 @@
;(function (define) {
(function(define) {
'use strict';
define([
'gettext',
@@ -6,7 +6,7 @@
'teams/js/views/team_utils',
'common/js/components/views/paging_header',
'common/js/components/views/paginated_view'
], function (gettext, TopicCardView, TeamUtils, PagingHeader, PaginatedView) {
], function(gettext, TopicCardView, TeamUtils, PagingHeader, PaginatedView) {
var TopicsView = PaginatedView.extend({
type: 'topics',
@@ -15,7 +15,7 @@
text: gettext('All topics')
},
initialize: function (options) {
initialize: function(options) {
this.options = _.extend({}, options);
this.itemViewClass = TopicCardView.extend({
router: options.router,
@@ -24,7 +24,7 @@
PaginatedView.prototype.initialize.call(this);
},
createHeaderView: function () {
createHeaderView: function() {
return new PagingHeader({
collection: this.options.collection,
srInfo: this.srInfo,

View File

@@ -1,6 +1,6 @@
var Markdown;
if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module
if (typeof exports === 'object' && typeof require === 'function') // we're in a CommonJS (e.g. Node.js) module
Markdown = exports;
else
Markdown = {};
@@ -50,8 +50,7 @@ else
// file before uncommenting it.
//
(function () {
(function() {
function identity(x) { return x; }
function returnFalse(x) { return false; }
@@ -59,25 +58,25 @@ else
HookCollection.prototype = {
chain: function (hookname, func) {
chain: function(hookname, func) {
var original = this[hookname];
if (!original)
throw new Error("unknown hook " + hookname);
throw new Error('unknown hook ' + hookname);
if (original === identity)
this[hookname] = func;
else
this[hookname] = function (x) { return func(original(x)); }
this[hookname] = function(x) { return func(original(x)); };
},
set: function (hookname, func) {
set: function(hookname, func) {
if (!this[hookname])
throw new Error("unknown hook " + hookname);
throw new Error('unknown hook ' + hookname);
this[hookname] = func;
},
addNoop: function (hookname) {
addNoop: function(hookname) {
this[hookname] = identity;
},
addFalse: function (hookname) {
addFalse: function(hookname) {
this[hookname] = returnFalse;
}
};
@@ -93,19 +92,19 @@ else
// to be a problem)
function SaveHash() { }
SaveHash.prototype = {
set: function (key, value) {
this["s_" + key] = value;
set: function(key, value) {
this['s_' + key] = value;
},
get: function (key) {
return this["s_" + key];
get: function(key) {
return this['s_' + key];
}
};
Markdown.Converter = function () {
Markdown.Converter = function() {
var pluginHooks = this.hooks = new HookCollection();
pluginHooks.addNoop("plainLinkText"); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
pluginHooks.addNoop("preConversion"); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked
pluginHooks.addNoop("postConversion"); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml
pluginHooks.addNoop('plainLinkText'); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
pluginHooks.addNoop('preConversion'); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked
pluginHooks.addNoop('postConversion'); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml
//
// Private state of the converter instance:
@@ -120,8 +119,7 @@ else
// (see _ProcessListItems() for details):
var g_list_level;
this.makeHtml = function (text) {
this.makeHtml = function(text) {
//
// Main function. The order in which other subs are called here is
// essential. Link and image substitutions need to happen before
@@ -132,8 +130,8 @@ else
// This will only happen if makeHtml on the same converter instance is called from a plugin hook.
// Don't do that.
if (g_urls)
throw new Error("Recursive call to converter.makeHtml");
throw new Error('Recursive call to converter.makeHtml');
// Create the private state objects.
g_urls = new SaveHash();
g_titles = new SaveHash();
@@ -146,19 +144,19 @@ else
// This lets us use tilde as an escape char to avoid md5 hashes
// The choice of character is arbitray; anything that isn't
// magic in Markdown will work.
text = text.replace(/~/g, "~T");
text = text.replace(/~/g, '~T');
// attacklab: Replace $ with ~D
// RegExp interprets $ as a special character
// when it's in a replacement string
text = text.replace(/\$/g, "~D");
text = text.replace(/\$/g, '~D');
// Standardize line endings
text = text.replace(/\r\n/g, "\n"); // DOS to Unix
text = text.replace(/\r/g, "\n"); // Mac to Unix
text = text.replace(/\r\n/g, '\n'); // DOS to Unix
text = text.replace(/\r/g, '\n'); // Mac to Unix
// Make sure text begins and ends with a couple of newlines:
text = "\n\n" + text + "\n\n";
text = '\n\n' + text + '\n\n';
// Convert all tabs to spaces.
text = _Detab(text);
@@ -167,7 +165,7 @@ else
// This makes subsequent regexen easier to write, because we can
// match consecutive blank lines with /\n+/ instead of something
// contorted like /[ \t]*\n+/ .
text = text.replace(/^[ \t]+$/mg, "");
text = text.replace(/^[ \t]+$/mg, '');
// Turn block-level HTML blocks into hash entries
text = _HashHTMLBlocks(text);
@@ -180,10 +178,10 @@ else
text = _UnescapeSpecialChars(text);
// attacklab: Restore dollar signs
text = text.replace(/~D/g, "$$");
text = text.replace(/~D/g, '$$');
// attacklab: Restore tildes
text = text.replace(/~T/g, "~");
text = text.replace(/~T/g, '~');
text = pluginHooks.postConversion(text);
@@ -224,7 +222,7 @@ else
*/
text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm,
function (wholeMatch, m1, m2, m3, m4, m5) {
function(wholeMatch, m1, m2, m3, m4, m5) {
m1 = m1.toLowerCase();
g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive
if (m4) {
@@ -232,27 +230,26 @@ else
// Put back the parenthetical statement we stole.
return m3;
} else if (m5) {
g_titles.set(m1, m5.replace(/"/g, "&quot;"));
g_titles.set(m1, m5.replace(/"/g, '&quot;'));
}
// Completely remove the definition from the text
return "";
return '';
}
);
return text;
}
function _HashHTMLBlocks(text) {
function _HashHTMLBlocks(text) {
// Hashify HTML blocks:
// We only want to do this for block-level HTML tags, such as headers,
// lists, and tables. That's because we still want to wrap <p>s around
// "paragraphs" that are wrapped in non-block-level tags, such as anchors,
// phrase emphasis, and spans. The list of tags we're looking for is
// hard-coded:
var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
var block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del';
var block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math';
// First, look for nested blocks, e.g.:
// <div>
@@ -305,7 +302,7 @@ else
text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement);
// Special case just for <hr />. It was easier to make a special case than
// to make the other regex more complicated.
// to make the other regex more complicated.
/*
text = text.replace(/
@@ -368,13 +365,13 @@ else
var blockText = m1;
// Undo double lines
blockText = blockText.replace(/^\n+/, "");
blockText = blockText.replace(/^\n+/, '');
// strip trailing blank lines
blockText = blockText.replace(/\n+$/g, "");
blockText = blockText.replace(/\n+$/g, '');
// Replace the element text with a marker ("~KxK" where x is its key)
blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n";
blockText = '\n\n~K' + (g_html_blocks.push(blockText) - 1) + 'K\n\n';
return blockText;
}
@@ -387,7 +384,7 @@ else
text = _DoHeaders(text);
// Do Horizontal Rules:
var replacement = "<hr />\n";
var replacement = '<hr />\n';
text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement);
text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement);
text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement);
@@ -425,14 +422,14 @@ else
// Must come after _DoAnchors(), because you can use < and >
// delimiters in inline links like [this](<url>).
text = _DoAutoLinks(text);
text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now
text = text.replace(/~P/g, '://'); // put in place to prevent autolinking; reset now
text = _EncodeAmpsAndAngles(text);
text = _DoItalicsAndBold(text);
// Do hard breaks:
text = text.replace(/ +\n/g, " <br>\n");
text = text.replace(/ +\n/g, ' <br>\n');
return text;
}
@@ -443,16 +440,16 @@ else
// don't conflict with their use in Markdown for code, italics and strong.
//
// Build a regex to find HTML tags and comments. See Friedl's
// Build a regex to find HTML tags and comments. See Friedl's
// "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
// SE: changed the comment part of the regex
var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi;
text = text.replace(regex, function (wholeMatch) {
var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`");
tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987
text = text.replace(regex, function(wholeMatch) {
var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
tag = escapeCharacters(tag, wholeMatch.charAt(1) == '!' ? '\\`*_/' : '\\`*_'); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987
return tag;
});
@@ -517,7 +514,7 @@ else
|
[^()\s]
)*?
)>?
)>?
[ \t]*
( // $5
(['"]) // quote char = $6
@@ -554,19 +551,19 @@ else
}
function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
if (m7 == undefined) m7 = "";
if (m7 == undefined) m7 = '';
var whole_match = m1;
var link_text = m2.replace(/:\/\//g, "~P"); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs
var link_text = m2.replace(/:\/\//g, '~P'); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs
var link_id = m3.toLowerCase();
var url = m4;
var title = m7;
if (url == "") {
if (link_id == "") {
if (url == '') {
if (link_id == '') {
// lower-case and turn embedded newlines into spaces
link_id = link_text.toLowerCase().replace(/ ?\n/g, " ");
link_id = link_text.toLowerCase().replace(/ ?\n/g, ' ');
}
url = "#" + link_id;
url = '#' + link_id;
if (g_urls.get(link_id) != undefined) {
url = g_urls.get(link_id);
@@ -577,22 +574,22 @@ else
else {
if (whole_match.search(/\(\s*\)$/m) > -1) {
// Special case for explicit empty url
url = "";
url = '';
} else {
return whole_match;
}
}
}
url = attributeSafeUrl(url);
var result = "<a href=\"" + url + "\"";
var result = '<a href="' + url + '"';
if (title != "") {
if (title != '') {
title = attributeEncode(title);
title = escapeCharacters(title, "*_");
result += " title=\"" + title + "\"";
title = escapeCharacters(title, '*_');
result += ' title="' + title + '"';
}
result += ">" + link_text + "</a>";
result += '>' + link_text + '</a>';
return result;
}
@@ -655,11 +652,11 @@ else
return text;
}
function attributeEncode(text) {
// unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title)
// never makes sense to have verbatim HTML in it (and the sanitizer would totally break it)
return text.replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
return text.replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}
function writeImageTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
@@ -669,14 +666,14 @@ else
var url = m4;
var title = m7;
if (!title) title = "";
if (!title) title = '';
if (url == "") {
if (link_id == "") {
if (url == '') {
if (link_id == '') {
// lower-case and turn embedded newlines into spaces
link_id = alt_text.toLowerCase().replace(/ ?\n/g, " ");
link_id = alt_text.toLowerCase().replace(/ ?\n/g, ' ');
}
url = "#" + link_id;
url = '#' + link_id;
if (g_urls.get(link_id) != undefined) {
url = g_urls.get(link_id);
@@ -688,40 +685,39 @@ else
return whole_match;
}
}
alt_text = escapeCharacters(attributeEncode(alt_text), "*_[]()");
url = escapeCharacters(url, "*_");
var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
alt_text = escapeCharacters(attributeEncode(alt_text), '*_[]()');
url = escapeCharacters(url, '*_');
var result = '<img src="' + url + '" alt="' + alt_text + '"';
// attacklab: Markdown.pl adds empty title attributes to images.
// Replicate this bug.
//if (title != "") {
// if (title != "") {
title = attributeEncode(title);
title = escapeCharacters(title, "*_");
result += " title=\"" + title + "\"";
//}
title = escapeCharacters(title, '*_');
result += ' title="' + title + '"';
// }
result += " />";
result += ' />';
return result;
}
function _DoHeaders(text) {
function _DoHeaders(text) {
// Setext-style headers:
// Header 1
// ========
//
//
// Header 2
// --------
//
text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
function (wholeMatch, m1) { return "<h1>" + _RunSpanGamut(m1) + "</h1>\n\n"; }
function(wholeMatch, m1) { return '<h1>' + _RunSpanGamut(m1) + '</h1>\n\n'; }
);
text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
function (matchFound, m1) { return "<h2>" + _RunSpanGamut(m1) + "</h2>\n\n"; }
function(matchFound, m1) { return '<h2>' + _RunSpanGamut(m1) + '</h2>\n\n'; }
);
// atx-style headers:
@@ -744,9 +740,9 @@ else
*/
text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
function (wholeMatch, m1, m2) {
function(wholeMatch, m1, m2) {
var h_level = m1.length;
return "<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">\n\n";
return '<h' + h_level + '>' + _RunSpanGamut(m2) + '</h' + h_level + '>\n\n';
}
);
@@ -760,7 +756,7 @@ else
// attacklab: add sentinel to hack around khtml/safari bug:
// http://bugs.webkit.org/show_bug.cgi?id=11231
text += "~0";
text += '~0';
// Re-usable pattern to match any entirel ul or ol list:
@@ -789,9 +785,9 @@ else
var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
if (g_list_level) {
text = text.replace(whole_list, function (wholeMatch, m1, m2) {
text = text.replace(whole_list, function(wholeMatch, m1, m2) {
var list = m1;
var list_type = (m2.search(/[*+-]/g) > -1) ? "ul" : "ol";
var list_type = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
var result = _ProcessListItems(list, list_type);
@@ -799,30 +795,30 @@ else
// up on the preceding line, to get it past the current stupid
// HTML block parser. This is a hack to work around the terrible
// hack that is the HTML block parser.
result = result.replace(/\s+$/, "");
result = "<" + list_type + ">" + result + "</" + list_type + ">\n";
result = result.replace(/\s+$/, '');
result = '<' + list_type + '>' + result + '</' + list_type + '>\n';
return result;
});
} else {
whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) {
text = text.replace(whole_list, function(wholeMatch, m1, m2, m3) {
var runup = m1;
var list = m2;
var list_type = (m3.search(/[*+-]/g) > -1) ? "ul" : "ol";
var list_type = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
var result = _ProcessListItems(list, list_type);
result = runup + "<" + list_type + ">\n" + result + "</" + list_type + ">\n";
result = runup + '<' + list_type + '>\n' + result + '</' + list_type + '>\n';
return result;
});
}
// attacklab: strip sentinel
text = text.replace(/~0/, "");
text = text.replace(/~0/, '');
return text;
}
var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" };
var _listItemMarkers = {ol: '\\d+[.]', ul: '[*+-]'};
function _ProcessListItems(list_str, list_type) {
//
@@ -855,10 +851,10 @@ else
g_list_level++;
// trim trailing blank lines:
list_str = list_str.replace(/\n{2,}$/, "\n");
list_str = list_str.replace(/\n{2,}$/, '\n');
// attacklab: add sentinel to emulate \z
list_str += "~0";
list_str += '~0';
// In the original attacklab showdown, list_type was not given to this function, and anything
// that matched /[*+-]|\d+[.]/ would just create the next <li>, causing this mismatch:
@@ -871,7 +867,7 @@ else
//
// We changed this to behave identical to MarkdownSharp. This is the constructed RegEx,
// with {MARKER} being one of \d+[.] or [*+-], depending on list_type:
/*
list_str = list_str.replace(/
(^[ \t]*) // leading whitespace = $1
@@ -886,10 +882,10 @@ else
*/
var marker = _listItemMarkers[list_type];
var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm");
var re = new RegExp('(^[ \\t]*)(' + marker + ')[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(' + marker + ')[ \\t]+))', 'gm');
var last_item_had_a_double_newline = false;
list_str = list_str.replace(re,
function (wholeMatch, m1, m2, m3) {
function(wholeMatch, m1, m2, m3) {
var item = m3;
var leading_space = m1;
var ends_with_double_newline = /\n\n$/.test(item);
@@ -901,16 +897,16 @@ else
else {
// Recursion for sub-lists:
item = _DoLists(_Outdent(item));
item = item.replace(/\n$/, ""); // chomp(item)
item = item.replace(/\n$/, ''); // chomp(item)
item = _RunSpanGamut(item);
}
last_item_had_a_double_newline = ends_with_double_newline;
return "<li>" + item + "</li>\n";
return '<li>' + item + '</li>\n';
}
);
// attacklab: strip sentinel
list_str = list_str.replace(/~0/g, "");
list_str = list_str.replace(/~0/g, '');
g_list_level--;
return list_str;
@@ -919,7 +915,7 @@ else
function _DoCodeBlocks(text) {
//
// Process Markdown `<pre><code>` blocks.
//
//
/*
text = text.replace(/
@@ -935,58 +931,58 @@ else
*/
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += "~0";
text += '~0';
text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
function (wholeMatch, m1, m2) {
function(wholeMatch, m1, m2) {
var codeblock = m1;
var nextChar = m2;
codeblock = _EncodeCode(_Outdent(codeblock));
codeblock = _Detab(codeblock);
codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
codeblock = '<pre><code>' + codeblock + '\n</code></pre>';
return "\n\n" + codeblock + "\n\n" + nextChar;
return '\n\n' + codeblock + '\n\n' + nextChar;
}
);
// attacklab: strip sentinel
text = text.replace(/~0/, "");
text = text.replace(/~0/, '');
return text;
}
function hashBlock(text) {
text = text.replace(/(^\n+|\n+$)/g, "");
return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n";
text = text.replace(/(^\n+|\n+$)/g, '');
return '\n\n~K' + (g_html_blocks.push(text) - 1) + 'K\n\n';
}
function _DoCodeSpans(text) {
//
// * Backtick quotes are used for <code></code> spans.
//
//
// * You can use multiple backticks as the delimiters if you want to
// include literal backticks in the code span. So, this input:
//
//
// Just type ``foo `bar` baz`` at the prompt.
//
//
// Will translate to:
//
//
// <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
//
//
// There's no arbitrary limit to the number of backticks you
// can use as delimters. If you need three consecutive backticks
// in your code, use four for delimiters, etc.
//
// * You can use spaces to get literal backticks at the edges:
//
//
// ... type `` `bar` `` ...
//
//
// Turns to:
//
//
// ... type <code>`bar`</code> ...
//
@@ -1004,13 +1000,13 @@ else
*/
text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
function (wholeMatch, m1, m2, m3, m4) {
function(wholeMatch, m1, m2, m3, m4) {
var c = m3;
c = c.replace(/^([ \t]*)/g, ""); // leading whitespace
c = c.replace(/[ \t]*$/g, ""); // trailing whitespace
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
c = _EncodeCode(c);
c = c.replace(/:\/\//g, "~P"); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs.
return m1 + "<code>" + c + "</code>";
c = c.replace(/:\/\//g, '~P'); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs.
return m1 + '<code>' + c + '</code>';
}
);
@@ -1025,14 +1021,14 @@ else
//
// Encode all ampersands; HTML entities are not
// entities within a Markdown code span.
text = text.replace(/&/g, "&amp;");
text = text.replace(/&/g, '&amp;');
// Do the angle bracket song and dance:
text = text.replace(/</g, "&lt;");
text = text.replace(/>/g, "&gt;");
text = text.replace(/</g, '&lt;');
text = text.replace(/>/g, '&gt;');
// Now, escape characters that are magic in Markdown:
text = escapeCharacters(text, "\*_{}[]\\", false);
text = escapeCharacters(text, '\*_{}[]\\', false);
// jj the line above breaks this:
//---
@@ -1047,20 +1043,18 @@ else
return text;
}
function _DoItalicsAndBold(text) {
function _DoItalicsAndBold(text) {
// <strong> must go first:
text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,
"$1<strong>$3</strong>$4");
'$1<strong>$3</strong>$4');
text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,
"$1<em>$3</em>$4");
'$1<em>$3</em>$4');
return text;
}
function _DoBlockQuotes(text) {
function _DoBlockQuotes(text) {
/*
text = text.replace(/
( // Wrap whole match in $1
@@ -1075,33 +1069,33 @@ else
*/
text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
function (wholeMatch, m1) {
function(wholeMatch, m1) {
var bq = m1;
// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting
bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
// attacklab: clean up hack
bq = bq.replace(/~0/g, "");
bq = bq.replace(/~0/g, '');
bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines
bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
bq = _RunBlockGamut(bq); // recurse
bq = bq.replace(/(^|\n)/g, "$1 ");
bq = bq.replace(/(^|\n)/g, '$1 ');
// These leading spaces screw with <pre> content, so we need to fix that:
bq = bq.replace(
/(\s*<pre>[^\r]+?<\/pre>)/gm,
function (wholeMatch, m1) {
function(wholeMatch, m1) {
var pre = m1;
// attacklab: hack around Konqueror 3.5.4 bug:
pre = pre.replace(/^ /mg, "~0");
pre = pre.replace(/~0/g, "");
pre = pre.replace(/^ /mg, '~0');
pre = pre.replace(/~0/g, '');
return pre;
});
return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
return hashBlock('<blockquote>\n' + bq + '\n</blockquote>');
}
);
return text;
@@ -1114,12 +1108,12 @@ else
//
// Strip leading and trailing lines:
text = text.replace(/^\n+/g, "");
text = text.replace(/\n+$/g, "");
text = text.replace(/^\n+/g, '');
text = text.replace(/\n+$/g, '');
var grafs = text.split(/\n{2,}/g);
var grafsOut = [];
var markerRe = /~K(\d+)K/;
//
@@ -1135,11 +1129,10 @@ else
}
else if (/\S/.test(str)) {
str = _RunSpanGamut(str);
str = str.replace(/^([ \t]*)/g, "<p>");
str += "</p>"
str = str.replace(/^([ \t]*)/g, '<p>');
str += '</p>';
grafsOut.push(str);
}
}
}
//
// Unhashify HTML blocks
@@ -1150,14 +1143,14 @@ else
var foundAny = true;
while (foundAny) { // we may need several runs, since the data may be nested
foundAny = false;
grafsOut[i] = grafsOut[i].replace(/~K(\d+)K/g, function (wholeMatch, id) {
grafsOut[i] = grafsOut[i].replace(/~K(\d+)K/g, function(wholeMatch, id) {
foundAny = true;
return g_html_blocks[id];
});
}
}
}
return grafsOut.join("\n\n");
return grafsOut.join('\n\n');
}
function _EncodeAmpsAndAngles(text) {
@@ -1165,10 +1158,10 @@ else
// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
// http://bumppo.net/projects/amputator/
text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&amp;");
text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
// Encode naked <'s
text = text.replace(/<(?![a-z\/?\$!])/gi, "&lt;");
text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
return text;
}
@@ -1194,18 +1187,17 @@ else
return text;
}
function _DoAutoLinks(text) {
function _DoAutoLinks(text) {
// note that at this point, all other URL in the text are already hyperlinked as <a href=""></a>
// *except* for the <http://www.foo.com> case
// automatically add < and > around unadorned raw hyperlinks
// must be preceded by space/BOF and followed by non-word/EOF character
text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4");
text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, '$1<$2$3>$4');
// autolink anything like <http://example.com>
var replacer = function (wholematch, m1) { return "<a href=\"" + m1 + "\">" + pluginHooks.plainLinkText(m1) + "</a>"; }
var replacer = function(wholematch, m1) { return '<a href="' + m1 + '">' + pluginHooks.plainLinkText(m1) + '</a>'; };
text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer);
// Email addresses: <address@domain.foo>
@@ -1237,7 +1229,7 @@ else
// Swap back in all the special characters we've hidden.
//
text = text.replace(/~E(\d+)E/g,
function (wholeMatch, m1) {
function(wholeMatch, m1) {
var charCodeToReplace = parseInt(m1);
return String.fromCharCode(charCodeToReplace);
}
@@ -1253,10 +1245,10 @@ else
// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width
text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
// attacklab: clean up hack
text = text.replace(/~0/g, "")
text = text.replace(/~0/g, '');
return text;
}
@@ -1265,12 +1257,12 @@ else
if (!/\t/.test(text))
return text;
var spaces = [" ", " ", " ", " "],
skew = 0,
v;
var spaces = [' ', ' ', ' ', ' '],
skew = 0,
v;
return text.replace(/[\n\t]/g, function (match, offset) {
if (match === "\n") {
return text.replace(/[\n\t]/g, function(match, offset) {
if (match === '\n') {
skew = offset + 1;
return match;
}
@@ -1287,20 +1279,20 @@ else
// Replacing encodeProblemUrlChars with attributeSafeUrl. See more at https://code.google.com/p/pagedown/source/detail?r=016a78c093843e203de5364117d34d406a09e8c0
function attributeSafeUrl(url) {
url = attributeEncode(url);
url = escapeCharacters(url, "*_:()[]")
url = escapeCharacters(url, '*_:()[]');
return url;
}
function escapeCharacters(text, charsToEscape, afterBackslash) {
// First we have to escape the escape characters so that
// we can build a character class out of them
var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])";
var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
if (afterBackslash) {
regexString = "\\\\" + regexString;
regexString = '\\\\' + regexString;
}
var regex = new RegExp(regexString, "g");
var regex = new RegExp(regexString, 'g');
text = text.replace(regex, escapeCharacters_callback);
return text;
@@ -1309,9 +1301,7 @@ else
function escapeCharacters_callback(wholeMatch, m1) {
var charCodeToEscape = m1.charCodeAt(0);
return "~E" + charCodeToEscape + "E";
}
}; // end of the Markdown.Converter constructor
return '~E' + charCodeToEscape + 'E';
}
}; // end of the Markdown.Converter constructor
})();

View File

@@ -1,14 +1,13 @@
// needs Markdown.Converter.js at the moment
(function () {
(function() {
var util = {},
position = {},
ui = {},
doc = window.document,
re = window.RegExp,
nav = window.navigator,
SETTINGS = { lineLength: 72 },
SETTINGS = {lineLength: 72},
// Used to work around some browser bugs where we can't use feature testing.
uaSniffed = {
@@ -26,30 +25,30 @@
// -------------------------------------------------------------------
// The text that appears on the dialog box when entering links.
var linkDialogText = gettext("Insert Hyperlink"),
var linkDialogText = gettext('Insert Hyperlink'),
linkUrlHelpText = gettext("e.g. 'http://google.com/'"),
linkDestinationLabel = gettext("Link Description"),
linkDestinationLabel = gettext('Link Description'),
linkDestinationHelpText = gettext("e.g. 'google'"),
linkDestinationError = gettext("Please provide a description of the link destination."),
linkDefaultText = "http://"; // The default text that appears in input
linkDestinationError = gettext('Please provide a description of the link destination.'),
linkDefaultText = 'http://'; // The default text that appears in input
// The text that appears on the dialog box when entering Images.
var imageDialogText = gettext("Insert Image (upload file or type URL)"),
var imageDialogText = gettext('Insert Image (upload file or type URL)'),
imageUrlHelpText = gettext("Type in a URL or use the \"Choose File\" button to upload a file from your machine. (e.g. 'http://example.com/img/clouds.jpg')"), // eslint-disable-line max-len
imageDescriptionLabel = gettext("Image Description"),
imageDefaultText = "http://", // The default text that appears in input
imageDescriptionLabel = gettext('Image Description'),
imageDefaultText = 'http://', // The default text that appears in input
imageDescError = gettext('Please describe this image or agree that it has no contextual value by checking the checkbox.'), // eslint-disable-line max-len
imageDescriptionHelpText = gettext("e.g. 'Sky with clouds'. The description is helpful for users who cannot see the image."), // eslint-disable-line max-len
imageDescriptionHelpLink = {
href: 'http://www.w3.org/TR/html5/embedded-content-0.html#alt',
text: gettext("How to create useful text alternatives.")
text: gettext('How to create useful text alternatives.')
},
imageIsDecorativeLabel = gettext('This image is for decorative purposes only and does not require a description.'); // eslint-disable-line max-len
// Text that is shared between both link and image dialog boxes.
var defaultHelpHoverTitle = gettext("Markdown Editing Help"),
urlLabel = gettext("URL"),
urlError = gettext("Please provide a valid URL.");
var defaultHelpHoverTitle = gettext('Markdown Editing Help'),
urlLabel = gettext('URL'),
urlError = gettext('Please provide a valid URL.');
// -------------------------------------------------------------------
// END OF YOUR CHANGES
@@ -63,55 +62,53 @@
// - getConverter() returns the markdown converter object that was passed to the constructor
// - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op.
// - refreshPreview() forces the preview to be updated. This method is only available after run() was called.
Markdown.Editor = function (markdownConverter, idPostfix, help, imageUploadHandler) {
idPostfix = idPostfix || "";
Markdown.Editor = function(markdownConverter, idPostfix, help, imageUploadHandler) {
idPostfix = idPostfix || '';
var hooks = this.hooks = new Markdown.HookCollection();
hooks.addNoop("onPreviewPush"); // called with no arguments after the preview has been refreshed
hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text
hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates
hooks.addNoop('onPreviewPush'); // called with no arguments after the preview has been refreshed
hooks.addNoop('postBlockquoteCreation'); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text
hooks.addFalse('insertImageDialog'); /* called with one parameter: a callback to be called with the URL of the image. If the application creates
* its own image insertion dialog, this hook should return true, and the callback should be called with the chosen
* image url (or null if the user cancelled). If this hook returns false, the default dialog will be used.
*/
this.util = util;
this.getConverter = function () { return markdownConverter; }
this.getConverter = function() { return markdownConverter; };
var that = this,
panels;
this.run = function () {
this.run = function() {
if (panels)
return; // already initialized
panels = new PanelCollection(idPostfix);
var commandManager = new CommandManager(hooks);
var previewManager = new PreviewManager(markdownConverter, panels, function (text, previewSet) { hooks.onPreviewPush(text, previewSet); });
var previewManager = new PreviewManager(markdownConverter, panels, function(text, previewSet) { hooks.onPreviewPush(text, previewSet); });
var undoManager, uiManager;
if (!/\?noundo/.test(doc.location.href)) {
undoManager = new UndoManager(function () {
undoManager = new UndoManager(function() {
previewManager.refresh();
if (uiManager) // not available on the first call
uiManager.setUndoRedoButtonStates();
}, panels);
this.textOperation = function (f) {
this.textOperation = function(f) {
undoManager.setCommandMode();
f();
that.refreshPreview();
}
};
}
uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, help, imageUploadHandler);
uiManager.setUndoRedoButtonStates();
var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); };
var forceRefresh = that.refreshPreview = function() { previewManager.refresh(true); };
forceRefresh();
};
}
};
};
// before: contains all the text in the input box BEFORE the selection.
// after: contains all the text in the input box AFTER the selection.
@@ -119,46 +116,43 @@
// startRegex: a regular expression to find the start tag
// endRegex: a regular expresssion to find the end tag
Chunks.prototype.findTags = function (startRegex, endRegex) {
Chunks.prototype.findTags = function(startRegex, endRegex) {
var chunkObj = this;
var regex;
if (startRegex) {
regex = util.extendRegExp(startRegex, "", "$");
if (startRegex) {
regex = util.extendRegExp(startRegex, '', '$');
this.before = this.before.replace(regex,
function (match) {
function(match) {
chunkObj.startTag = chunkObj.startTag + match;
return "";
return '';
});
regex = util.extendRegExp(startRegex, "^", "");
regex = util.extendRegExp(startRegex, '^', '');
this.selection = this.selection.replace(regex,
function (match) {
function(match) {
chunkObj.startTag = chunkObj.startTag + match;
return "";
return '';
});
}
if (endRegex) {
regex = util.extendRegExp(endRegex, "", "$");
if (endRegex) {
regex = util.extendRegExp(endRegex, '', '$');
this.selection = this.selection.replace(regex,
function (match) {
function(match) {
chunkObj.endTag = match + chunkObj.endTag;
return "";
return '';
});
regex = util.extendRegExp(endRegex, "^", "");
regex = util.extendRegExp(endRegex, '^', '');
this.after = this.after.replace(regex,
function (match) {
function(match) {
chunkObj.endTag = match + chunkObj.endTag;
return "";
return '';
});
}
};
@@ -167,21 +161,20 @@
// to the before/after regions.
//
// If remove is true, the whitespace disappears.
Chunks.prototype.trimWhitespace = function (remove) {
Chunks.prototype.trimWhitespace = function(remove) {
var beforeReplacer, afterReplacer, that = this;
if (remove) {
beforeReplacer = afterReplacer = "";
beforeReplacer = afterReplacer = '';
} else {
beforeReplacer = function (s) { that.before += s; return ""; }
afterReplacer = function (s) { that.after = s + that.after; return ""; }
beforeReplacer = function(s) { that.before += s; return ''; };
afterReplacer = function(s) { that.after = s + that.after; return ''; };
}
this.selection = this.selection.replace(/^(\s*)/, beforeReplacer).replace(/(\s*)$/, afterReplacer);
};
Chunks.prototype.skipLines = function (nLinesBefore, nLinesAfter, findExtraNewlines) {
Chunks.prototype.skipLines = function(nLinesBefore, nLinesAfter, findExtraNewlines) {
if (nLinesBefore === undefined) {
nLinesBefore = 1;
}
@@ -198,48 +191,46 @@
// chrome bug ... documented at: http://meta.stackoverflow.com/questions/63307/blockquote-glitch-in-editor-in-chrome-6-and-7/65985#65985
if (navigator.userAgent.match(/Chrome/)) {
"X".match(/()./);
'X'.match(/()./);
}
this.selection = this.selection.replace(/(^\n*)/, "");
this.selection = this.selection.replace(/(^\n*)/, '');
this.startTag = this.startTag + re.$1;
this.selection = this.selection.replace(/(\n*$)/, "");
this.selection = this.selection.replace(/(\n*$)/, '');
this.endTag = this.endTag + re.$1;
this.startTag = this.startTag.replace(/(^\n*)/, "");
this.startTag = this.startTag.replace(/(^\n*)/, '');
this.before = this.before + re.$1;
this.endTag = this.endTag.replace(/(\n*$)/, "");
this.endTag = this.endTag.replace(/(\n*$)/, '');
this.after = this.after + re.$1;
if (this.before) {
regexText = replacementText = "";
if (this.before) {
regexText = replacementText = '';
while (nLinesBefore--) {
regexText += "\\n?";
replacementText += "\n";
regexText += '\\n?';
replacementText += '\n';
}
if (findExtraNewlines) {
regexText = "\\n*";
regexText = '\\n*';
}
this.before = this.before.replace(new re(regexText + "$", ""), replacementText);
this.before = this.before.replace(new re(regexText + '$', ''), replacementText);
}
if (this.after) {
regexText = replacementText = "";
if (this.after) {
regexText = replacementText = '';
while (nLinesAfter--) {
regexText += "\\n?";
replacementText += "\n";
regexText += '\\n?';
replacementText += '\n';
}
if (findExtraNewlines) {
regexText = "\\n*";
regexText = '\\n*';
}
this.after = this.after.replace(new re(regexText, ""), replacementText);
this.after = this.after.replace(new re(regexText, ''), replacementText);
}
};
@@ -262,10 +253,10 @@
// and 8) and ONLY on button clicks. Keyboard shortcuts work
// normally since the focus never leaves the textarea.
function PanelCollection(postfix) {
this.buttonBar = doc.getElementById("wmd-button-bar" + postfix);
this.preview = doc.getElementById("wmd-preview" + postfix);
this.input = doc.getElementById("wmd-input" + postfix);
};
this.buttonBar = doc.getElementById('wmd-button-bar' + postfix);
this.preview = doc.getElementById('wmd-preview' + postfix);
this.input = doc.getElementById('wmd-input' + postfix);
}
util.isValidUrl = function(url) {
return /^((?:http|https|ftp):\/{2}|\/)[^]+$/.test(url);
@@ -273,25 +264,24 @@
// Returns true if the DOM element is visible, false if it's hidden.
// Checks if display is anything other than none.
util.isVisible = function (elem) {
util.isVisible = function(elem) {
if (window.getComputedStyle) {
// Most browsers
return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none";
return window.getComputedStyle(elem, null).getPropertyValue('display') !== 'none';
}
else if (elem.currentStyle) {
// IE
return elem.currentStyle["display"] !== "none";
return elem.currentStyle['display'] !== 'none';
}
};
// Adds a listener callback to a DOM element which is fired on a specified
// event.
util.addEvent = function (elem, event, listener) {
util.addEvent = function(elem, event, listener) {
if (elem.attachEvent) {
// IE only. The "on" is mandatory.
elem.attachEvent("on" + event, listener);
elem.attachEvent('on' + event, listener);
}
else {
// Other browsers.
@@ -302,10 +292,10 @@
// Removes a listener callback from a DOM element which is fired on a specified
// event.
util.removeEvent = function (elem, event, listener) {
util.removeEvent = function(elem, event, listener) {
if (elem.detachEvent) {
// IE only. The "on" is mandatory.
elem.detachEvent("on" + event, listener);
elem.detachEvent('on' + event, listener);
}
else {
// Other browsers.
@@ -314,9 +304,9 @@
};
// Converts \r\n and \r to \n.
util.fixEolChars = function (text) {
text = text.replace(/\r\n/g, "\n");
text = text.replace(/\r/g, "\n");
util.fixEolChars = function(text) {
text = text.replace(/\r\n/g, '\n');
text = text.replace(/\r/g, '\n');
return text;
};
@@ -328,35 +318,34 @@
// The flags are unchanged.
//
// regex is a RegExp, pre and post are strings.
util.extendRegExp = function (regex, pre, post) {
util.extendRegExp = function(regex, pre, post) {
if (pre === null || pre === undefined) {
pre = "";
pre = '';
}
if (post === null || post === undefined) {
post = "";
post = '';
}
var pattern = regex.toString();
var flags;
// Replace the flags with empty space and store them.
pattern = pattern.replace(/\/([gim]*)$/, function (wholeMatch, flagsPart) {
pattern = pattern.replace(/\/([gim]*)$/, function(wholeMatch, flagsPart) {
flags = flagsPart;
return "";
return '';
});
// Remove the slash delimiters on the regular expression.
pattern = pattern.replace(/(^\/|\/$)/g, "");
pattern = pattern.replace(/(^\/|\/$)/g, '');
pattern = pre + pattern + post;
return new re(pattern, flags);
}
};
// UNFINISHED
// The assignment in the while loop makes jslint cranky.
// I'll change it to a better loop later.
position.getTop = function (elem, isInner) {
position.getTop = function(elem, isInner) {
var result = elem.offsetTop;
if (!isInner) {
while (elem = elem.offsetParent) {
@@ -366,16 +355,15 @@
return result;
};
position.getHeight = function (elem) {
position.getHeight = function(elem) {
return elem.offsetHeight || elem.scrollHeight;
};
position.getWidth = function (elem) {
position.getWidth = function(elem) {
return elem.offsetWidth || elem.scrollWidth;
};
position.getPageSize = function () {
position.getPageSize = function() {
var scrollWidth, scrollHeight;
var innerWidth, innerHeight;
@@ -416,18 +404,17 @@
// Handles pushing and popping TextareaStates for undo/redo commands.
// I should rename the stack variables to list.
function UndoManager(callback, panels) {
function UndoManager(callback, panels) {
var undoObj = this;
var undoStack = []; // A stack of undo states
var stackPtr = 0; // The index of the current state
var mode = "none";
var mode = 'none';
var lastState; // The last state
var timer; // The setTimeout handle for cancelling the timer
var inputStateObj;
// Set the mode for later logic steps.
var setMode = function (newMode, noSave) {
var setMode = function(newMode, noSave) {
if (mode != newMode) {
mode = newMode;
if (!noSave) {
@@ -435,7 +422,7 @@
}
}
if (!uaSniffed.isIE || mode != "moving") {
if (!uaSniffed.isIE || mode != 'moving') {
timer = setTimeout(refreshState, 1);
}
else {
@@ -443,22 +430,22 @@
}
};
var refreshState = function (isInitialState) {
var refreshState = function(isInitialState) {
inputStateObj = new TextareaState(panels, isInitialState);
timer = undefined;
};
this.setCommandMode = function () {
mode = "command";
this.setCommandMode = function() {
mode = 'command';
saveState();
timer = setTimeout(refreshState, 0);
};
this.canUndo = function () {
this.canUndo = function() {
return stackPtr > 1;
};
this.canRedo = function () {
this.canRedo = function() {
if (undoStack[stackPtr + 1]) {
return true;
}
@@ -466,8 +453,7 @@
};
// Removes the last state and restores it.
this.undo = function () {
this.undo = function() {
if (undoObj.canUndo()) {
if (lastState) {
// What about setting state -1 to null or checking for undefined?
@@ -484,16 +470,14 @@
}
}
mode = "none";
mode = 'none';
panels.input.focus();
refreshState();
};
// Redo an action.
this.redo = function () {
if (undoObj.canRedo()) {
this.redo = function() {
if (undoObj.canRedo()) {
undoStack[++stackPtr].restore();
if (callback) {
@@ -501,19 +485,19 @@
}
}
mode = "none";
mode = 'none';
panels.input.focus();
refreshState();
};
// Push the input area state to the stack.
var saveState = function () {
var saveState = function() {
var currState = inputStateObj || new TextareaState(panels);
if (!currState) {
return false;
}
if (mode == "moving") {
if (mode == 'moving') {
if (!lastState) {
lastState = currState;
}
@@ -532,32 +516,30 @@
}
};
var handleCtrlYZ = function (event) {
var handleCtrlYZ = function(event) {
var handled = false;
if (event.ctrlKey || event.metaKey) {
if (event.ctrlKey || event.metaKey) {
// IE and Opera do not support charCode.
var keyCode = event.charCode || event.keyCode;
var keyCodeChar = String.fromCharCode(keyCode);
switch (keyCodeChar) {
case "y":
undoObj.redo();
handled = true;
break;
case 'y':
undoObj.redo();
handled = true;
break;
case "z":
if (!event.shiftKey) {
undoObj.undo();
}
else {
undoObj.redo();
}
handled = true;
break;
case 'z':
if (!event.shiftKey) {
undoObj.undo();
}
else {
undoObj.redo();
}
handled = true;
break;
}
}
@@ -573,43 +555,41 @@
};
// Set the mode depending on what is going on in the input area.
var handleModeChange = function (event) {
if (!event.ctrlKey && !event.metaKey) {
var handleModeChange = function(event) {
if (!event.ctrlKey && !event.metaKey) {
var keyCode = event.keyCode;
if ((keyCode >= 33 && keyCode <= 40) || (keyCode >= 63232 && keyCode <= 63235)) {
// 33 - 40: page up/dn and arrow keys
// 63232 - 63235: page up/dn and arrow keys on safari
setMode("moving");
setMode('moving');
}
else if (keyCode == 8 || keyCode == 46 || keyCode == 127) {
// 8: backspace
// 46: delete
// 127: delete
setMode("deleting");
setMode('deleting');
}
else if (keyCode == 13) {
// 13: Enter
setMode("newlines");
setMode('newlines');
}
else if (keyCode == 27) {
// 27: escape
setMode("escape");
setMode('escape');
}
else if ((keyCode < 16 || keyCode > 20) && keyCode != 91) {
// 16-20 are shift, etc.
// 91: left window key
// I think this might be a little messed up since there are
// a lot of nonprinting keys above 20.
setMode("typing");
setMode('typing');
}
}
};
var setEventHandlers = function () {
util.addEvent(panels.input, "keypress", function (event) {
var setEventHandlers = function() {
util.addEvent(panels.input, 'keypress', function(event) {
// keyCode 89: y
// keyCode 90: z
if ((event.ctrlKey || event.metaKey) && (event.keyCode == 89 || event.keyCode == 90)) {
@@ -617,27 +597,27 @@
}
});
var handlePaste = function () {
var handlePaste = function() {
if (uaSniffed.isIE || (inputStateObj && inputStateObj.text != panels.input.value)) {
if (timer == undefined) {
mode = "paste";
mode = 'paste';
saveState();
refreshState();
}
}
};
util.addEvent(panels.input, "keydown", handleCtrlYZ);
util.addEvent(panels.input, "keydown", handleModeChange);
util.addEvent(panels.input, "mousedown", function () {
setMode("moving");
util.addEvent(panels.input, 'keydown', handleCtrlYZ);
util.addEvent(panels.input, 'keydown', handleModeChange);
util.addEvent(panels.input, 'mousedown', function() {
setMode('moving');
});
panels.input.onpaste = handlePaste;
panels.input.ondrop = handlePaste;
};
var init = function () {
var init = function() {
setEventHandlers();
refreshState(true);
saveState();
@@ -650,12 +630,11 @@
// The input textarea state/contents.
// This is used to implement undo/redo by the undo manager.
function TextareaState(panels, isInitialState) {
function TextareaState(panels, isInitialState) {
// Aliases
var stateObj = this;
var inputArea = panels.input;
this.init = function () {
this.init = function() {
if (!util.isVisible(inputArea)) {
return;
}
@@ -667,50 +646,43 @@
this.scrollTop = inputArea.scrollTop;
if (!this.text && inputArea.selectionStart || inputArea.selectionStart === 0) {
this.text = inputArea.value;
}
}
}
};
// Sets the selected text in the input box after we've performed an
// operation.
this.setInputAreaSelection = function () {
this.setInputAreaSelection = function() {
if (!util.isVisible(inputArea)) {
return;
}
if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) {
if (inputArea.selectionStart !== undefined && !uaSniffed.isOpera) {
inputArea.focus();
inputArea.selectionStart = stateObj.start;
inputArea.selectionEnd = stateObj.end;
inputArea.scrollTop = stateObj.scrollTop;
}
else if (doc.selection) {
else if (doc.selection) {
if (doc.activeElement && doc.activeElement !== inputArea) {
return;
}
inputArea.focus();
var range = inputArea.createTextRange();
range.moveStart("character", -inputArea.value.length);
range.moveEnd("character", -inputArea.value.length);
range.moveEnd("character", stateObj.end);
range.moveStart("character", stateObj.start);
range.moveStart('character', -inputArea.value.length);
range.moveEnd('character', -inputArea.value.length);
range.moveEnd('character', stateObj.end);
range.moveStart('character', stateObj.start);
range.select();
}
};
this.setInputAreaSelectionStartEnd = function () {
if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) {
this.setInputAreaSelectionStartEnd = function() {
if (!panels.ieCachedRange && (inputArea.selectionStart || inputArea.selectionStart === 0)) {
stateObj.start = inputArea.selectionStart;
stateObj.end = inputArea.selectionEnd;
}
else if (doc.selection) {
else if (doc.selection) {
stateObj.text = util.fixEolChars(inputArea.value);
// IE loses the selection in the textarea when buttons are
@@ -719,12 +691,12 @@
var range = panels.ieCachedRange || doc.selection.createRange();
var fixedRange = util.fixEolChars(range.text);
var marker = "\x07";
var marker = '\x07';
var markedRange = marker + fixedRange + marker;
range.text = markedRange;
var inputText = util.fixEolChars(inputArea.value);
range.moveStart("character", -markedRange.length);
range.moveStart('character', -markedRange.length);
range.text = fixedRange;
stateObj.start = inputText.indexOf(marker);
@@ -733,9 +705,9 @@
var len = stateObj.text.length - util.fixEolChars(inputArea.value).length;
if (len) {
range.moveStart("character", -fixedRange.length);
range.moveStart('character', -fixedRange.length);
while (len--) {
fixedRange += "\n";
fixedRange += '\n';
stateObj.end += 1;
}
range.text = fixedRange;
@@ -751,8 +723,7 @@
};
// Restore this state into the input area.
this.restore = function () {
this.restore = function() {
if (stateObj.text != undefined && stateObj.text != inputArea.value) {
inputArea.value = stateObj.text;
}
@@ -761,13 +732,12 @@
};
// Gets a collection of HTML chunks from the inptut textarea.
this.getChunks = function () {
this.getChunks = function() {
var chunk = new Chunks();
chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start));
chunk.startTag = "";
chunk.startTag = '';
chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end));
chunk.endTag = "";
chunk.endTag = '';
chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end));
chunk.scrollTop = stateObj.scrollTop;
@@ -775,8 +745,7 @@
};
// Sets the TextareaState properties given a chunk of markdown.
this.setChunks = function (chunk) {
this.setChunks = function(chunk) {
chunk.before = chunk.before + chunk.startTag;
chunk.after = chunk.endTag + chunk.after;
@@ -786,30 +755,27 @@
this.scrollTop = chunk.scrollTop;
};
this.init();
};
function PreviewManager(converter, panels, previewPushCallback) {
}
function PreviewManager(converter, panels, previewPushCallback) {
var managerObj = this;
var timeout;
var elapsedTime;
var oldInputText;
var maxDelay = 3000;
var startType = "delayed"; // The other legal value is "manual"
var startType = 'delayed'; // The other legal value is "manual"
// Adds event listeners to elements
var setupEvents = function (inputElem, listener) {
util.addEvent(inputElem, "input", listener);
var setupEvents = function(inputElem, listener) {
util.addEvent(inputElem, 'input', listener);
inputElem.onpaste = listener;
inputElem.ondrop = listener;
util.addEvent(inputElem, "keypress", listener);
util.addEvent(inputElem, "keydown", listener);
util.addEvent(inputElem, 'keypress', listener);
util.addEvent(inputElem, 'keydown', listener);
};
var getDocScrollTop = function () {
var getDocScrollTop = function() {
var result = 0;
if (window.innerHeight) {
@@ -827,8 +793,7 @@
return result;
};
var makePreviewHtml = function () {
var makePreviewHtml = function() {
// If there is no registered preview panel
// there is nothing to do.
if (!panels.preview)
@@ -856,18 +821,16 @@
};
// setTimeout is already used. Used as an event listener.
var applyTimeout = function () {
var applyTimeout = function() {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
if (startType !== "manual") {
if (startType !== 'manual') {
var delay = 0;
if (startType === "delayed") {
if (startType === 'delayed') {
delay = elapsedTime;
}
@@ -878,23 +841,22 @@
}
};
var getScaleFactor = function (panel) {
var getScaleFactor = function(panel) {
if (panel.scrollHeight <= panel.clientHeight) {
return 1;
}
return panel.scrollTop / (panel.scrollHeight - panel.clientHeight);
};
var setPanelScrollTops = function () {
var setPanelScrollTops = function() {
if (panels.preview) {
panels.preview.scrollTop = (panels.preview.scrollHeight - panels.preview.clientHeight) * getScaleFactor(panels.preview);
}
};
this.refresh = function (requiresRefresh) {
this.refresh = function(requiresRefresh) {
if (requiresRefresh) {
oldInputText = "";
oldInputText = '';
makePreviewHtml();
}
else {
@@ -902,7 +864,7 @@
}
};
this.processingTime = function () {
this.processingTime = function() {
return elapsedTime;
};
@@ -911,7 +873,7 @@
// IE doesn't let you use innerHTML if the element is contained somewhere in a table
// (which is the case for inline editing) -- in that case, detach the element, set the
// value, and reattach. Yes, that *is* ridiculous.
var ieSafePreviewSet = function (text) {
var ieSafePreviewSet = function(text) {
var preview = panels.preview;
var parent = preview.parentNode;
var sibling = preview.nextSibling;
@@ -921,15 +883,15 @@
parent.appendChild(preview);
else
parent.insertBefore(preview, sibling);
}
};
var nonSuckyBrowserPreviewSet = function (text) {
var nonSuckyBrowserPreviewSet = function(text) {
panels.preview.innerHTML = text;
}
};
var previewSetter;
var previewSet = function (text) {
var previewSet = function(text) {
if (previewSetter)
return previewSetter(text);
@@ -942,8 +904,7 @@
}
};
var pushPreviewHtml = function (text) {
var pushPreviewHtml = function(text) {
var emptyTop = position.getTop(panels.input) - getDocScrollTop();
if (panels.preview) {
@@ -960,7 +921,7 @@
var fullTop = position.getTop(panels.input) - getDocScrollTop();
if (uaSniffed.isIE) {
setTimeout(function () {
setTimeout(function() {
window.scrollBy(0, fullTop - emptyTop);
}, 0);
}
@@ -969,8 +930,7 @@
}
};
var init = function () {
var init = function() {
setupEvents(panels.input, applyTimeout);
makePreviewHtml();
@@ -980,41 +940,40 @@
};
init();
};
}
// Creates the background behind the hyperlink text entry box.
// And download dialog
// Most of this has been moved to CSS but the div creation and
// browser-specific hacks remain here.
ui.createBackground = function () {
var background = doc.createElement("div"),
ui.createBackground = function() {
var background = doc.createElement('div'),
style = background.style;
background.className = "wmd-prompt-background";
background.className = 'wmd-prompt-background';
style.position = "absolute";
style.top = "0";
style.position = 'absolute';
style.top = '0';
style.zIndex = "1000";
style.zIndex = '1000';
if (uaSniffed.isIE) {
style.filter = "alpha(opacity=50)";
style.filter = 'alpha(opacity=50)';
}
else {
style.opacity = "0.5";
style.opacity = '0.5';
}
var pageSize = position.getPageSize();
style.height = pageSize[1] + "px";
style.height = pageSize[1] + 'px';
if (uaSniffed.isIE) {
style.left = doc.documentElement.scrollLeft;
style.width = doc.documentElement.clientWidth;
}
else {
style.left = "0";
style.width = "100%";
style.left = '0';
style.width = '100%';
}
doc.body.appendChild(background);
@@ -1029,7 +988,7 @@
// callback: The function which is executed when the prompt is dismissed, either via OK or Cancel.
// It receives a single argument; either the entered text (if OK was chosen) or null (if Cancel
// was chosen).
ui.prompt = function (title,
ui.prompt = function(title,
urlLabel,
urlHelp,
urlError,
@@ -1040,8 +999,7 @@
defaultInputText,
callback,
imageIsDecorativeLabel,
imageUploadHandler) {
imageUploadHandler) {
// These variables need to be declared at this level since they are used
// in multiple functions.
var dialog, // The dialog box.
@@ -1054,14 +1012,14 @@
// Used as a keydown event handler. Esc dismisses the prompt.
// Key code 27 is ESC.
var checkEscape = function (key) {
var checkEscape = function(key) {
var code = (key.charCode || key.keyCode);
if (code === 27) {
close(true);
}
};
var clearFormErrorMessages = function () {
var clearFormErrorMessages = function() {
urlInput.classList.remove('has-error');
urlErrorMsg.style.display = 'none';
descInput.classList.remove('has-error');
@@ -1071,8 +1029,8 @@
// Dismisses the hyperlink input box.
// isCancel is true if we don't care about the input text.
// isCancel is false if we are going to keep the text.
var close = function (isCancel) {
util.removeEvent(doc.body, "keydown", checkEscape);
var close = function(isCancel) {
util.removeEvent(doc.body, 'keydown', checkEscape);
var url = urlInput.value.trim();
var description = descInput.value.trim();
@@ -1130,66 +1088,66 @@
};
// Create the text input box form/window.
var createDialog = function () {
var createDialog = function() {
// The main dialog box.
dialog = doc.createElement("div");
dialog = doc.createElement('div');
dialog.innerHTML = _.template(
document.getElementById("customwmd-prompt-template").innerHTML)({
title: title,
uploadFieldClass: (imageUploadHandler ? 'file-upload' : ''),
urlLabel: urlLabel,
urlError: urlError,
urlHelp: urlHelp,
urlDescLabel: urlDescLabel,
descError: urlDescError,
urlDescHelp: urlDescHelp,
urlDescHelpLink: urlDescHelpLink,
okText: gettext("OK"),
cancelText: gettext("Cancel"),
chooseFileText: gettext("Choose File"),
imageIsDecorativeLabel: imageIsDecorativeLabel,
imageUploadHandler: imageUploadHandler
});
dialog.setAttribute("role", "dialog");
dialog.setAttribute("tabindex", "-1");
dialog.setAttribute("aria-labelledby", "editorDialogTitle");
dialog.className = "wmd-prompt-dialog";
dialog.style.padding = "10px;";
dialog.style.position = "fixed";
dialog.style.width = "500px";
dialog.style.zIndex = "1001";
document.getElementById('customwmd-prompt-template').innerHTML)({
title: title,
uploadFieldClass: (imageUploadHandler ? 'file-upload' : ''),
urlLabel: urlLabel,
urlError: urlError,
urlHelp: urlHelp,
urlDescLabel: urlDescLabel,
descError: urlDescError,
urlDescHelp: urlDescHelp,
urlDescHelpLink: urlDescHelpLink,
okText: gettext('OK'),
cancelText: gettext('Cancel'),
chooseFileText: gettext('Choose File'),
imageIsDecorativeLabel: imageIsDecorativeLabel,
imageUploadHandler: imageUploadHandler
});
dialog.setAttribute('role', 'dialog');
dialog.setAttribute('tabindex', '-1');
dialog.setAttribute('aria-labelledby', 'editorDialogTitle');
dialog.className = 'wmd-prompt-dialog';
dialog.style.padding = '10px;';
dialog.style.position = 'fixed';
dialog.style.width = '500px';
dialog.style.zIndex = '1001';
doc.body.appendChild(dialog);
// This has to be done AFTER adding the dialog to the form if you
// want it to be centered.
util.addEvent(doc.body, "keydown", checkEscape);
dialog.style.top = "50%";
dialog.style.left = "50%";
dialog.style.display = "block";
util.addEvent(doc.body, 'keydown', checkEscape);
dialog.style.top = '50%';
dialog.style.left = '50%';
dialog.style.display = 'block';
if (uaSniffed.isIE_5or6) {
dialog.style.position = "absolute";
dialog.style.top = doc.documentElement.scrollTop + 200 + "px";
dialog.style.left = "50%";
dialog.style.position = 'absolute';
dialog.style.top = doc.documentElement.scrollTop + 200 + 'px';
dialog.style.left = '50%';
}
dialog.style.marginTop = -(position.getHeight(dialog) / 2) + "px";
dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + "px";
dialog.style.marginTop = -(position.getHeight(dialog) / 2) + 'px';
dialog.style.marginLeft = -(position.getWidth(dialog) / 2) + 'px';
urlInput = document.getElementById("new-url-input");
urlErrorMsg = document.getElementById("new-url-input-field-message");
descInput = document.getElementById("new-url-desc-input");
descErrorMsg = document.getElementById("new-url-desc-input-field-message");
urlInput = document.getElementById('new-url-input');
urlErrorMsg = document.getElementById('new-url-input-field-message');
descInput = document.getElementById('new-url-desc-input');
descErrorMsg = document.getElementById('new-url-desc-input-field-message');
urlInput.value = defaultInputText;
okButton = document.getElementById("new-link-image-ok");
cancelButton = document.getElementById("new-link-image-cancel");
okButton = document.getElementById('new-link-image-ok');
cancelButton = document.getElementById('new-link-image-cancel');
okButton.onclick = function () { return close(false); };
cancelButton.onclick = function () { return close(true); };
okButton.onclick = function() { return close(false); };
cancelButton.onclick = function() { return close(true); };
if(imageUploadHandler) {
var startUploadHandler = function () {
document.getElementById("file-upload").onchange = function() {
if (imageUploadHandler) {
var startUploadHandler = function() {
document.getElementById('file-upload').onchange = function() {
imageUploadHandler(this, urlInput);
urlInput.focus();
@@ -1198,17 +1156,17 @@
};
};
startUploadHandler();
document.getElementById("file-upload-proxy").onclick = function () {
document.getElementById("file-upload").click();
document.getElementById('file-upload-proxy').onclick = function() {
document.getElementById('file-upload').click();
return false;
};
document.getElementById("img-is-decorative").onchange = function () {
document.getElementById('img-is-decorative').onchange = function() {
descInput.required = !descInput.required;
};
}
// trap focus in the dialog box
$(dialog).on("keydown", function (event) {
$(dialog).on('keydown', function(event) {
// On tab backward from the first tabbable item in the prompt
if (event.which === 9 && event.shiftKey && event.target === urlInput) {
event.preventDefault();
@@ -1225,8 +1183,7 @@
// Why is this in a zero-length timeout?
// Is it working around a browser bug?
setTimeout(function () {
setTimeout(function() {
createDialog();
var defTextLen = defaultInputText.length;
@@ -1237,8 +1194,8 @@
else if (urlInput.createTextRange) {
var range = urlInput.createTextRange();
range.collapse(false);
range.moveStart("character", -defTextLen);
range.moveEnd("character", defTextLen);
range.moveStart('character', -defTextLen);
range.moveEnd('character', defTextLen);
range.select();
}
@@ -1246,70 +1203,67 @@
}, 0);
};
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, imageUploadHandler) {
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, imageUploadHandler) {
var inputBox = panels.input,
buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements.
makeSpritedButtonRow();
var keyEvent = "keydown";
var keyEvent = 'keydown';
if (uaSniffed.isOpera) {
keyEvent = "keypress";
keyEvent = 'keypress';
}
util.addEvent(inputBox, keyEvent, function (key) {
util.addEvent(inputBox, keyEvent, function(key) {
// Check to see if we have a button key and, if so execute the callback.
if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) {
if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) {
var keyCode = key.charCode || key.keyCode;
var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
switch (keyCodeStr) {
case "b":
doClick(buttons.bold);
break;
case "i":
doClick(buttons.italic);
break;
case "l":
doClick(buttons.link);
break;
case "q":
doClick(buttons.quote);
break;
case "k":
doClick(buttons.code);
break;
case "g":
doClick(buttons.image);
break;
case "o":
doClick(buttons.olist);
break;
case "u":
doClick(buttons.ulist);
break;
case "h":
doClick(buttons.heading);
break;
case "r":
doClick(buttons.hr);
break;
case "y":
case 'b':
doClick(buttons.bold);
break;
case 'i':
doClick(buttons.italic);
break;
case 'l':
doClick(buttons.link);
break;
case 'q':
doClick(buttons.quote);
break;
case 'k':
doClick(buttons.code);
break;
case 'g':
doClick(buttons.image);
break;
case 'o':
doClick(buttons.olist);
break;
case 'u':
doClick(buttons.ulist);
break;
case 'h':
doClick(buttons.heading);
break;
case 'r':
doClick(buttons.hr);
break;
case 'y':
doClick(buttons.redo);
break;
case 'z':
if (key.shiftKey) {
doClick(buttons.redo);
break;
case "z":
if (key.shiftKey) {
doClick(buttons.redo);
}
else {
doClick(buttons.undo);
}
break;
default:
return;
}
else {
doClick(buttons.undo);
}
break;
default:
return;
}
@@ -1324,13 +1278,13 @@
});
// Auto-indent on shift-enter
util.addEvent(inputBox, "keyup", function (key) {
util.addEvent(inputBox, 'keyup', function(key) {
if (key.shiftKey && !key.ctrlKey && !key.metaKey) {
var keyCode = key.charCode || key.keyCode;
// Character 13 is Enter
if (keyCode === 13) {
var fakeButton = {};
fakeButton.textOp = bindCommand("doAutoindent");
fakeButton.textOp = bindCommand('doAutoindent');
doClick(fakeButton);
}
}
@@ -1338,7 +1292,7 @@
// special handler because IE clears the context of the textbox on ESC
if (uaSniffed.isIE) {
util.addEvent(inputBox, "keydown", function (key) {
util.addEvent(inputBox, 'keydown', function(key) {
var code = key.keyCode;
if (code === 27) {
return false;
@@ -1348,12 +1302,10 @@
// Perform the button's action.
function doClick(button) {
function doClick(button) {
inputBox.focus();
if (button.textOp) {
if (button.textOp) {
if (undoManager) {
undoManager.setCommandMode();
}
@@ -1383,8 +1335,7 @@
// Yes this is awkward and I think it sucks, but there's
// no real workaround. Only the image and link code
// create dialogs and require the function pointers.
var fixupInputArea = function () {
var fixupInputArea = function() {
inputBox.focus();
if (chunks) {
@@ -1399,36 +1350,34 @@
if (!noCleanup) {
fixupInputArea();
}
}
}
if (button.execute) {
button.execute(undoManager);
}
};
}
function setupButton(button, isEnabled) {
var normalYShift = "0px";
var disabledYShift = "-20px";
var highlightYShift = "-40px";
var image = button.getElementsByTagName("span")[0];
function setupButton(button, isEnabled) {
var normalYShift = '0px';
var disabledYShift = '-20px';
var highlightYShift = '-40px';
var image = button.getElementsByTagName('span')[0];
if (isEnabled) {
image.style.backgroundPosition = button.XShift + " " + normalYShift;
button.onmouseover = function () {
image.style.backgroundPosition = this.XShift + " " + highlightYShift;
image.style.backgroundPosition = button.XShift + ' ' + normalYShift;
button.onmouseover = function() {
image.style.backgroundPosition = this.XShift + ' ' + highlightYShift;
};
button.onmouseout = function () {
image.style.backgroundPosition = this.XShift + " " + normalYShift;
button.onmouseout = function() {
image.style.backgroundPosition = this.XShift + ' ' + normalYShift;
};
// IE tries to select the background image "button" text (it's
// implemented in a list item) so we have to cache the selection
// on mousedown.
if (uaSniffed.isIE) {
button.onmousedown = function () {
button.onmousedown = function() {
if (doc.activeElement && doc.activeElement !== panels.input) { // we're not even in the input box, so there's no selection
return;
}
@@ -1438,14 +1387,14 @@
}
if (!button.isHelp) {
button.onclick = function () {
button.onclick = function() {
if (this.onmouseout) {
this.onmouseout();
}
doClick(this);
return false;
}
util.addEvent(button, "keydown", function(event) {
};
util.addEvent(button, 'keydown', function(event) {
var keyCode = event.charCode || event.keyCode;
if (keyCode == 32 || keyCode == 13) {
if (event.preventDefault) {
@@ -1456,43 +1405,42 @@
}
doClick(button);
}
})
});
}
}
else {
image.style.backgroundPosition = button.XShift + " " + disabledYShift;
button.onmouseover = button.onmouseout = button.onclick = function () { };
image.style.backgroundPosition = button.XShift + ' ' + disabledYShift;
button.onmouseover = button.onmouseout = button.onclick = function() { };
}
}
function bindCommand(method) {
if (typeof method === "string")
if (typeof method === 'string')
method = commandManager[method];
return function () { method.apply(commandManager, arguments); }
return function() { method.apply(commandManager, arguments); };
}
function makeSpritedButtonRow() {
function makeSpritedButtonRow() {
var buttonBar = panels.buttonBar;
var normalYShift = "0px";
var disabledYShift = "-20px";
var highlightYShift = "-40px";
var normalYShift = '0px';
var disabledYShift = '-20px';
var highlightYShift = '-40px';
var buttonRow = document.createElement("div");
buttonRow.setAttribute("role", "toolbar");
buttonRow.id = "wmd-button-row" + postfix;
var buttonRow = document.createElement('div');
buttonRow.setAttribute('role', 'toolbar');
buttonRow.id = 'wmd-button-row' + postfix;
buttonRow.className = 'wmd-button-row';
buttonRow = buttonBar.appendChild(buttonRow);
var xPosition = 0;
var makeButton = function (id, title, XShift, textOp) {
var button = document.createElement("span");
button.setAttribute("role", "button");
var makeButton = function(id, title, XShift, textOp) {
var button = document.createElement('span');
button.setAttribute('role', 'button');
button.tabIndex = 0;
button.className = "wmd-button";
button.style.left = xPosition + "px";
button.className = 'wmd-button';
button.style.left = xPosition + 'px';
xPosition += 25;
var buttonImage = document.createElement("span");
var buttonImage = document.createElement('span');
button.id = id + postfix;
button.appendChild(buttonImage);
button.title = title;
@@ -1503,55 +1451,55 @@
buttonRow.appendChild(button);
return button;
};
var makeSpacer = function (num) {
var spacer = document.createElement("span");
spacer.setAttribute("role", "separator");
spacer.className = "wmd-spacer wmd-spacer" + num;
spacer.id = "wmd-spacer" + num + postfix;
var makeSpacer = function(num) {
var spacer = document.createElement('span');
spacer.setAttribute('role', 'separator');
spacer.className = 'wmd-spacer wmd-spacer' + num;
spacer.id = 'wmd-spacer' + num + postfix;
buttonRow.appendChild(spacer);
xPosition += 25;
}
};
buttons.bold = makeButton("wmd-bold-button", gettext("Bold (Ctrl+B)"), "0px", bindCommand("doBold"));
buttons.italic = makeButton("wmd-italic-button", gettext("Italic (Ctrl+I)"), "-20px", bindCommand("doItalic"));
buttons.bold = makeButton('wmd-bold-button', gettext('Bold (Ctrl+B)'), '0px', bindCommand('doBold'));
buttons.italic = makeButton('wmd-italic-button', gettext('Italic (Ctrl+I)'), '-20px', bindCommand('doItalic'));
makeSpacer(1);
buttons.link = makeButton("wmd-link-button", gettext("Hyperlink (Ctrl+L)"), "-40px", bindCommand(function (chunk, postProcessing) {
buttons.link = makeButton('wmd-link-button', gettext('Hyperlink (Ctrl+L)'), '-40px', bindCommand(function(chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, false);
}));
buttons.quote = makeButton("wmd-quote-button", gettext("Blockquote (Ctrl+Q)"), "-60px", bindCommand("doBlockquote"));
buttons.code = makeButton("wmd-code-button", gettext("Code Sample (Ctrl+K)"), "-80px", bindCommand("doCode"));
buttons.image = makeButton("wmd-image-button", gettext("Image (Ctrl+G)"), "-100px", bindCommand(function (chunk, postProcessing) {
buttons.quote = makeButton('wmd-quote-button', gettext('Blockquote (Ctrl+Q)'), '-60px', bindCommand('doBlockquote'));
buttons.code = makeButton('wmd-code-button', gettext('Code Sample (Ctrl+K)'), '-80px', bindCommand('doCode'));
buttons.image = makeButton('wmd-image-button', gettext('Image (Ctrl+G)'), '-100px', bindCommand(function(chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, true, imageUploadHandler);
}));
makeSpacer(2);
buttons.olist = makeButton("wmd-olist-button", gettext("Numbered List (Ctrl+O)"), "-120px", bindCommand(function (chunk, postProcessing) {
buttons.olist = makeButton('wmd-olist-button', gettext('Numbered List (Ctrl+O)'), '-120px', bindCommand(function(chunk, postProcessing) {
this.doList(chunk, postProcessing, true);
}));
buttons.ulist = makeButton("wmd-ulist-button", gettext("Bulleted List (Ctrl+U)"), "-140px", bindCommand(function (chunk, postProcessing) {
buttons.ulist = makeButton('wmd-ulist-button', gettext('Bulleted List (Ctrl+U)'), '-140px', bindCommand(function(chunk, postProcessing) {
this.doList(chunk, postProcessing, false);
}));
buttons.heading = makeButton("wmd-heading-button", gettext("Heading (Ctrl+H)"), "-160px", bindCommand("doHeading"));
buttons.hr = makeButton("wmd-hr-button", gettext("Horizontal Rule (Ctrl+R)"), "-180px", bindCommand("doHorizontalRule"));
buttons.heading = makeButton('wmd-heading-button', gettext('Heading (Ctrl+H)'), '-160px', bindCommand('doHeading'));
buttons.hr = makeButton('wmd-hr-button', gettext('Horizontal Rule (Ctrl+R)'), '-180px', bindCommand('doHorizontalRule'));
makeSpacer(3);
buttons.undo = makeButton("wmd-undo-button", gettext("Undo (Ctrl+Z)"), "-200px", null);
buttons.undo.execute = function (manager) { if (manager) manager.undo(); };
buttons.undo = makeButton('wmd-undo-button', gettext('Undo (Ctrl+Z)'), '-200px', null);
buttons.undo.execute = function(manager) { if (manager) manager.undo(); };
var redoTitle = /win/.test(nav.platform.toLowerCase()) ?
gettext("Redo (Ctrl+Y)") :
gettext("Redo (Ctrl+Shift+Z)"); // mac and other non-Windows platforms
gettext('Redo (Ctrl+Y)') :
gettext('Redo (Ctrl+Shift+Z)'); // mac and other non-Windows platforms
buttons.redo = makeButton("wmd-redo-button", redoTitle, "-220px", null);
buttons.redo.execute = function (manager) { if (manager) manager.redo(); };
buttons.redo = makeButton('wmd-redo-button', redoTitle, '-220px', null);
buttons.redo.execute = function(manager) { if (manager) manager.redo(); };
if (helpOptions) {
var helpButton = document.createElement("span");
var helpButtonImage = document.createElement("span");
var helpButton = document.createElement('span');
var helpButtonImage = document.createElement('span');
helpButton.appendChild(helpButtonImage);
helpButton.className = "wmd-button wmd-help-button";
helpButton.id = "wmd-help-button" + postfix;
helpButton.XShift = "-240px";
helpButton.className = 'wmd-button wmd-help-button';
helpButton.id = 'wmd-help-button' + postfix;
helpButton.XShift = '-240px';
helpButton.isHelp = true;
helpButton.style.right = "0px";
helpButton.style.right = '0px';
helpButton.title = helpOptions.title || defaultHelpHoverTitle;
helpButton.onclick = helpOptions.handler;
@@ -1568,10 +1516,9 @@
setupButton(buttons.undo, undoManager.canUndo());
setupButton(buttons.redo, undoManager.canRedo());
}
};
this.setUndoRedoButtonStates = setUndoRedoButtonStates;
}
this.setUndoRedoButtonStates = setUndoRedoButtonStates;
}
function CommandManager(pluginHooks) {
@@ -1581,45 +1528,44 @@
var commandProto = CommandManager.prototype;
// The markdown symbols - 4 spaces = code, > = blockquote, etc.
commandProto.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";
commandProto.prefixes = '(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)';
// Remove markdown symbols from the chunk selection.
commandProto.unwrap = function (chunk) {
var txt = new re("([^\\n])\\n(?!(\\n|" + this.prefixes + "))", "g");
chunk.selection = chunk.selection.replace(txt, "$1 $2");
commandProto.unwrap = function(chunk) {
var txt = new re('([^\\n])\\n(?!(\\n|' + this.prefixes + '))', 'g');
chunk.selection = chunk.selection.replace(txt, '$1 $2');
};
commandProto.wrap = function (chunk, len) {
commandProto.wrap = function(chunk, len) {
this.unwrap(chunk);
var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm"),
var regex = new re('(.{1,' + len + '})( +|$\\n?)', 'gm'),
that = this;
chunk.selection = chunk.selection.replace(regex, function (line, marked) {
if (new re("^" + that.prefixes, "").test(line)) {
chunk.selection = chunk.selection.replace(regex, function(line, marked) {
if (new re('^' + that.prefixes, '').test(line)) {
return line;
}
return marked + "\n";
return marked + '\n';
});
chunk.selection = chunk.selection.replace(/\s+$/, "");
chunk.selection = chunk.selection.replace(/\s+$/, '');
};
commandProto.doBold = function (chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 2, gettext("strong text"));
commandProto.doBold = function(chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 2, gettext('strong text'));
};
commandProto.doItalic = function (chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 1, gettext("emphasized text"));
commandProto.doItalic = function(chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 1, gettext('emphasized text'));
};
// chunk: The selected region that will be enclosed with */**
// nStars: 1 for italics, 2 for bold
// insertText: If you just click the button without highlighting text, this gets inserted
commandProto.doBorI = function (chunk, postProcessing, nStars, insertText) {
commandProto.doBorI = function(chunk, postProcessing, nStars, insertText) {
// Get rid of whitespace and fixup newlines.
chunk.trimWhitespace();
chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n");
chunk.selection = chunk.selection.replace(/\n{2,}/g, '\n');
// Look for stars before and after. Is the chunk already marked up?
// note that these regex matches cannot fail
@@ -1630,19 +1576,18 @@
// Remove stars if we have to since the button acts as a toggle.
if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) {
chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), "");
chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), "");
chunk.before = chunk.before.replace(re('[*]{' + nStars + '}$', ''), '');
chunk.after = chunk.after.replace(re('^[*]{' + nStars + '}', ''), '');
}
else if (!chunk.selection && starsAfter) {
// It's not really clear why this code is necessary. It just moves
// some arbitrary stuff around.
chunk.after = chunk.after.replace(/^([*_]*)/, "");
chunk.before = chunk.before.replace(/(\s?)$/, "");
chunk.after = chunk.after.replace(/^([*_]*)/, '');
chunk.before = chunk.before.replace(/(\s?)$/, '');
var whitespace = re.$1;
chunk.before = chunk.before + starsAfter + whitespace;
}
else {
else {
// In most cases, if you don't have any selected text and click the button
// you'll get a selected, marked up region with the default text inserted.
if (!chunk.selection && !starsAfter) {
@@ -1650,7 +1595,7 @@
}
// Add the true markup.
var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ?
var markup = nStars <= 1 ? '*' : '**'; // shouldn't the test be = ?
chunk.before = chunk.before + markup;
chunk.after = markup + chunk.after;
}
@@ -1658,24 +1603,22 @@
return;
};
commandProto.stripLinkDefs = function (text, defsToAdd) {
commandProto.stripLinkDefs = function(text, defsToAdd) {
text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,
function (totalMatch, id, link, newlines, title) {
defsToAdd[id] = totalMatch.replace(/\s*$/, "");
function(totalMatch, id, link, newlines, title) {
defsToAdd[id] = totalMatch.replace(/\s*$/, '');
if (newlines) {
// Strip the title and return that separately.
defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, "");
defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, '');
return newlines + title;
}
return "";
return '';
});
return text;
};
commandProto.addLinkDef = function (chunk, linkDef) {
commandProto.addLinkDef = function(chunk, linkDef) {
var refNumber = 0; // The current reference number
var defsToAdd = {}; //
// Start with a clean slate by removing all previous link definitions.
@@ -1683,13 +1626,13 @@
chunk.selection = this.stripLinkDefs(chunk.selection, defsToAdd);
chunk.after = this.stripLinkDefs(chunk.after, defsToAdd);
var defs = "";
var defs = '';
var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;
var addDefNumber = function (def) {
var addDefNumber = function(def) {
refNumber++;
def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:");
defs += "\n" + def;
def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, ' [' + refNumber + ']:');
defs += '\n' + def;
};
// note that
@@ -1697,7 +1640,7 @@
// of regex, inner is always a proper substring of wholeMatch, and
// b) more than one level of nesting is neither supported by the regex
// nor making a lot of sense (the only use case for nesting is a linked image)
var getLink = function (wholeMatch, before, inner, afterInner, id, end) {
var getLink = function(wholeMatch, before, inner, afterInner, id, end) {
inner = inner.replace(regex, getLink);
if (defsToAdd[id]) {
addDefNumber(defsToAdd[id]);
@@ -1720,13 +1663,13 @@
chunk.after = chunk.after.replace(regex, getLink);
if (chunk.after) {
chunk.after = chunk.after.replace(/\n*$/, "");
chunk.after = chunk.after.replace(/\n*$/, '');
}
if (!chunk.after) {
chunk.selection = chunk.selection.replace(/\n*$/, "");
chunk.selection = chunk.selection.replace(/\n*$/, '');
}
chunk.after += "\n\n" + defs;
chunk.after += '\n\n' + defs;
return refOut;
};
@@ -1734,42 +1677,39 @@
// takes the line as entered into the add link/as image dialog and makes
// sure the URL and the optinal title are "nice".
function properlyEncoded(linkdef) {
return linkdef.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/, function (wholematch, link, title) {
link = link.replace(/\?.*$/, function (querypart) {
return querypart.replace(/\+/g, " "); // in the query string, a plus and a space are identical
return linkdef.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/, function(wholematch, link, title) {
link = link.replace(/\?.*$/, function(querypart) {
return querypart.replace(/\+/g, ' '); // in the query string, a plus and a space are identical
});
link = decodeURIComponent(link); // unencode first, to prevent double encoding
link = encodeURI(link).replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29');
link = link.replace(/\?.*$/, function (querypart) {
return querypart.replace(/\+/g, "%2b"); // since we replaced plus with spaces in the query part, all pluses that now appear where originally encoded
link = link.replace(/\?.*$/, function(querypart) {
return querypart.replace(/\+/g, '%2b'); // since we replaced plus with spaces in the query part, all pluses that now appear where originally encoded
});
if (title) {
title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, "");
title = $.trim(title).replace(/"/g, "quot;").replace(/\(/g, "&#40;").replace(/\)/g, "&#41;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
title = title.trim ? title.trim() : title.replace(/^\s*/, '').replace(/\s*$/, '');
title = $.trim(title).replace(/"/g, 'quot;').replace(/\(/g, '&#40;').replace(/\)/g, '&#41;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
return title ? link + ' "' + title + '"' : link;
});
}
commandProto.doLinkOrImage = function (chunk, postProcessing, isImage, imageUploadHandler) {
commandProto.doLinkOrImage = function(chunk, postProcessing, isImage, imageUploadHandler) {
chunk.trimWhitespace();
chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
var background;
if (chunk.endTag.length > 1 && chunk.startTag.length > 0) {
chunk.startTag = chunk.startTag.replace(/!?\[/, "");
chunk.endTag = "";
this.addLinkDef(chunk, null);
if (chunk.endTag.length > 1 && chunk.startTag.length > 0) {
chunk.startTag = chunk.startTag.replace(/!?\[/, '');
chunk.endTag = '';
this.addLinkDef(chunk, null);
}
else {
else {
// We're moving start and end tag back into the selection, since (as we're in the else block) we're not
// *removing* a link, but *adding* one, so whatever findTags() found is now back to being part of the
// link text. linkEnteredCallback takes care of escaping any brackets.
chunk.selection = chunk.startTag + chunk.selection + chunk.endTag;
chunk.startTag = chunk.endTag = "";
chunk.startTag = chunk.endTag = '';
if (/\n\n/.test(chunk.selection)) {
this.addLinkDef(chunk, null);
@@ -1778,7 +1718,7 @@
var that = this;
// The function to be executed when you enter a link and press OK or Cancel.
// Marks up the link and adds the ref.
var linkEnteredCallback = function (link, description) {
var linkEnteredCallback = function(link, description) {
background.parentNode.removeChild(background);
if (link !== null) {
@@ -1800,20 +1740,20 @@
// this by anchoring with ^, because in the case that the selection starts with two brackets, this
// would mean a zero-width match at the start. Since zero-width matches advance the string position,
// the first bracket could then not act as the "not a backslash" for the second.
chunk.selection = (" " + chunk.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g, "$1\\").substr(1);
chunk.selection = (' ' + chunk.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g, '$1\\').substr(1);
var linkDef = " [999]: " + properlyEncoded(link);
var linkDef = ' [999]: ' + properlyEncoded(link);
var num = that.addLinkDef(chunk, linkDef);
chunk.startTag = isImage ? "![" : "[";
chunk.endTag = "][" + num + "]";
chunk.startTag = isImage ? '![' : '[';
chunk.endTag = '][' + num + ']';
if (!chunk.selection) {
if (isImage) {
chunk.selection = description ? description : "";
chunk.selection = description ? description : '';
}
else {
chunk.selection = description ? description : gettext("enter link description here");
chunk.selection = description ? description : gettext('enter link description here');
}
}
}
@@ -1860,23 +1800,22 @@
// When making a list, hitting shift-enter will put your cursor on the next line
// at the current indent level.
commandProto.doAutoindent = function (chunk, postProcessing) {
commandProto.doAutoindent = function(chunk, postProcessing) {
var commandMgr = this,
fakeSelection = false;
chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n");
chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n");
chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n");
chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, '\n\n');
chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, '\n\n');
chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, '\n\n');
// There's no selection, end the cursor wasn't at the end of the line:
// The user wants to split the current list item / code line / blockquote line
// (for the latter it doesn't really matter) in two. Temporarily select the
// (rest of the) line to achieve this.
if (!chunk.selection && !/^[ \t]*(?:\n|$)/.test(chunk.after)) {
chunk.after = chunk.after.replace(/^[^\n]*/, function (wholeMatch) {
chunk.after = chunk.after.replace(/^[^\n]*/, function(wholeMatch) {
chunk.selection = wholeMatch;
return "";
return '';
});
fakeSelection = true;
}
@@ -1899,27 +1838,26 @@
if (fakeSelection) {
chunk.after = chunk.selection + chunk.after;
chunk.selection = "";
chunk.selection = '';
}
};
commandProto.doBlockquote = function (chunk, postProcessing) {
commandProto.doBlockquote = function(chunk, postProcessing) {
chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,
function (totalMatch, newlinesBefore, text, newlinesAfter) {
function(totalMatch, newlinesBefore, text, newlinesAfter) {
chunk.before += newlinesBefore;
chunk.after = newlinesAfter + chunk.after;
return text;
});
chunk.before = chunk.before.replace(/(>[ \t]*)$/,
function (totalMatch, blankLine) {
function(totalMatch, blankLine) {
chunk.selection = blankLine + chunk.selection;
return "";
return '';
});
chunk.selection = chunk.selection.replace(/^(\s|>)+$/, "");
chunk.selection = chunk.selection || gettext("Blockquote");
chunk.selection = chunk.selection.replace(/^(\s|>)+$/, '');
chunk.selection = chunk.selection || gettext('Blockquote');
// The original code uses a regular expression to find out how much of the
// text *directly before* the selection already was a blockquote:
@@ -1951,11 +1889,11 @@
// Hence we replaced this by a simple state machine that just goes through the
// lines and checks for a), b), and c).
var match = "",
leftOver = "",
var match = '',
leftOver = '',
line;
if (chunk.before) {
var lines = chunk.before.replace(/\n$/, "").split("\n");
var lines = chunk.before.replace(/\n$/, '').split('\n');
var inChain = false;
for (var i = 0; i < lines.length; i++) {
var good = false;
@@ -1971,15 +1909,15 @@
good = inChain; // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain
}
if (good) {
match += line + "\n";
match += line + '\n';
} else {
leftOver += match + line;
match = "\n";
match = '\n';
}
}
if (!/(^|\n)>/.test(match)) { // d)
leftOver += match;
match = "";
match = '';
}
}
@@ -1989,50 +1927,49 @@
// end of change
if (chunk.after) {
chunk.after = chunk.after.replace(/^\n?/, "\n");
chunk.after = chunk.after.replace(/^\n?/, '\n');
}
chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,
function (totalMatch) {
function(totalMatch) {
chunk.endTag = totalMatch;
return "";
return '';
}
);
var replaceBlanksInTags = function (useBracket) {
var replacement = useBracket ? "> " : "";
var replaceBlanksInTags = function(useBracket) {
var replacement = useBracket ? '> ' : '';
if (chunk.startTag) {
chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/,
function (totalMatch, markdown) {
return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n";
function(totalMatch, markdown) {
return '\n' + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + '\n';
});
}
if (chunk.endTag) {
chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/,
function (totalMatch, markdown) {
return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n";
function(totalMatch, markdown) {
return '\n' + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + '\n';
});
}
};
if (/^(?![ ]{0,3}>)/m.test(chunk.selection)) {
this.wrap(chunk, SETTINGS.lineLength - 2);
chunk.selection = chunk.selection.replace(/^/gm, "> ");
chunk.selection = chunk.selection.replace(/^/gm, '> ');
replaceBlanksInTags(true);
chunk.skipLines();
} else {
chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, "");
chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, '');
this.unwrap(chunk);
replaceBlanksInTags(false);
if (!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag) {
chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n");
chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, '\n\n');
}
if (!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag) {
chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, "\n\n");
chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, '\n\n');
}
}
@@ -2040,26 +1977,24 @@
if (!/\n/.test(chunk.selection)) {
chunk.selection = chunk.selection.replace(/^(> *)/,
function (wholeMatch, blanks) {
function(wholeMatch, blanks) {
chunk.startTag += blanks;
return "";
return '';
});
}
};
commandProto.doCode = function (chunk, postProcessing) {
commandProto.doCode = function(chunk, postProcessing) {
var hasTextBefore = /\S[ ]*$/.test(chunk.before);
var hasTextAfter = /^[ ]*\S/.test(chunk.after);
// Use 'four space' markdown if the selection is on its own
// line or is multiline.
if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) {
if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) {
chunk.before = chunk.before.replace(/[ ]{4}$/,
function (totalMatch) {
function(totalMatch) {
chunk.selection = totalMatch + chunk.selection;
return "";
return '';
});
var nLinesBack = 1;
@@ -2075,18 +2010,18 @@
chunk.skipLines(nLinesBack, nLinesForward);
if (!chunk.selection) {
chunk.startTag = " ";
chunk.selection = gettext("enter code here");
chunk.startTag = ' ';
chunk.selection = gettext('enter code here');
}
else {
if (/^[ ]{0,3}\S/m.test(chunk.selection)) {
if (/\n/.test(chunk.selection))
chunk.selection = chunk.selection.replace(/^/gm, " ");
chunk.selection = chunk.selection.replace(/^/gm, ' ');
else // if it's not multiline, do not select the four added spaces; this is more consistent with the doList behavior
chunk.before += " ";
chunk.before += ' ';
}
else {
chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, "");
chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, '');
}
}
}
@@ -2097,23 +2032,22 @@
chunk.findTags(/`/, /`/);
if (!chunk.startTag && !chunk.endTag) {
chunk.startTag = chunk.endTag = "`";
chunk.startTag = chunk.endTag = '`';
if (!chunk.selection) {
chunk.selection = gettext("enter code here");
chunk.selection = gettext('enter code here');
}
}
else if (chunk.endTag && !chunk.startTag) {
chunk.before += chunk.endTag;
chunk.endTag = "";
chunk.endTag = '';
}
else {
chunk.startTag = chunk.endTag = "";
chunk.startTag = chunk.endTag = '';
}
}
};
commandProto.doList = function (chunk, postProcessing, isNumberedList) {
commandProto.doList = function(chunk, postProcessing, isNumberedList) {
// These are identical except at the very beginning and end.
// Should probably use the regex extension function to make this clearer.
var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;
@@ -2122,27 +2056,26 @@
// The default bullet is a dash but others are possible.
// This has nothing to do with the particular HTML bullet,
// it's just a markdown bullet.
var bullet = "-";
var bullet = '-';
// The number in a numbered list.
var num = 1;
// Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list.
var getItemPrefix = function () {
var getItemPrefix = function() {
var prefix;
if (isNumberedList) {
prefix = " " + num + ". ";
prefix = ' ' + num + '. ';
num++;
}
else {
prefix = " " + bullet + " ";
prefix = ' ' + bullet + ' ';
}
return prefix;
};
// Fixes the prefixes of the other list items.
var getPrefixedItem = function (itemText) {
var getPrefixedItem = function(itemText) {
// The numbering flag is unset when called by autoindent.
if (isNumberedList === undefined) {
isNumberedList = /^\s*\d/.test(itemText);
@@ -2150,7 +2083,7 @@
// Renumber/bullet the list element.
itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,
function (_) {
function(_) {
return getItemPrefix();
});
@@ -2161,14 +2094,13 @@
if (chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)) {
chunk.before += chunk.startTag;
chunk.startTag = "";
chunk.startTag = '';
}
if (chunk.startTag) {
if (chunk.startTag) {
var hasDigits = /\d+[.]/.test(chunk.startTag);
chunk.startTag = "";
chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n");
chunk.startTag = '';
chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, '\n');
this.unwrap(chunk);
chunk.skipLines();
@@ -2184,7 +2116,7 @@
var nLinesUp = 1;
chunk.before = chunk.before.replace(previousItemsRegex,
function (itemText) {
function(itemText) {
if (/^\s*([*+-])/.test(itemText)) {
bullet = re.$1;
}
@@ -2193,7 +2125,7 @@
});
if (!chunk.selection) {
chunk.selection = gettext("List item");
chunk.selection = gettext('List item');
}
var prefix = getItemPrefix();
@@ -2201,7 +2133,7 @@
var nLinesDown = 1;
chunk.after = chunk.after.replace(nextItemsRegex,
function (itemText) {
function(itemText) {
nLinesDown = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0;
return getPrefixedItem(itemText);
});
@@ -2209,24 +2141,22 @@
chunk.trimWhitespace(true);
chunk.skipLines(nLinesUp, nLinesDown, true);
chunk.startTag = prefix;
var spaces = prefix.replace(/./g, " ");
var spaces = prefix.replace(/./g, ' ');
this.wrap(chunk, SETTINGS.lineLength - spaces.length);
chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces);
chunk.selection = chunk.selection.replace(/\n/g, '\n' + spaces);
};
commandProto.doHeading = function (chunk, postProcessing) {
commandProto.doHeading = function(chunk, postProcessing) {
// Remove leading/trailing whitespace and reduce internal spaces to single spaces.
chunk.selection = chunk.selection.replace(/\s+/g, " ");
chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, "");
chunk.selection = chunk.selection.replace(/\s+/g, ' ');
chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, '');
// If we clicked the button with no selected text, we just
// make a level 2 hash header around some default text.
if (!chunk.selection) {
chunk.startTag = "## ";
chunk.selection = gettext("Heading");
chunk.endTag = " ##";
chunk.startTag = '## ';
chunk.selection = gettext('Heading');
chunk.endTag = ' ##';
return;
}
@@ -2237,7 +2167,7 @@
if (/#+/.test(chunk.startTag)) {
headerLevel = re.lastMatch.length;
}
chunk.startTag = chunk.endTag = "";
chunk.startTag = chunk.endTag = '';
// Try to get the current header level by looking for - and = in the line
// below the selection.
@@ -2250,7 +2180,7 @@
}
// Skip to the next line so we can create the header markdown.
chunk.startTag = chunk.endTag = "";
chunk.startTag = chunk.endTag = '';
chunk.skipLines(1, 1);
// We make a level 2 header if there is no current header.
@@ -2258,27 +2188,24 @@
// If it's already a level 1 header, it's removed.
var headerLevelToCreate = headerLevel == 0 ? 2 : headerLevel - 1;
if (headerLevelToCreate > 0) {
if (headerLevelToCreate > 0) {
// The button only creates level 1 and 2 underline headers.
// Why not have it iterate over hash header levels? Wouldn't that be easier and cleaner?
var headerChar = headerLevelToCreate >= 2 ? "-" : "=";
var headerChar = headerLevelToCreate >= 2 ? '-' : '=';
var len = chunk.selection.length;
if (len > SETTINGS.lineLength) {
len = SETTINGS.lineLength;
}
chunk.endTag = "\n";
chunk.endTag = '\n';
while (len--) {
chunk.endTag += headerChar;
}
}
};
commandProto.doHorizontalRule = function (chunk, postProcessing) {
chunk.startTag = "----------\n";
chunk.selection = "";
commandProto.doHorizontalRule = function(chunk, postProcessing) {
chunk.startTag = '----------\n';
chunk.selection = '';
chunk.skipLines(2, 1, true);
}
};
})();

View File

@@ -1,19 +1,19 @@
(function () {
(function() {
var output, Converter;
if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module
if (typeof exports === 'object' && typeof require === 'function') { // we're in a CommonJS (e.g. Node.js) module
output = exports;
Converter = require("./Markdown.Converter").Converter;
Converter = require('./Markdown.Converter').Converter;
} else {
output = window.Markdown;
Converter = output.Converter;
}
output.getSanitizingConverter = function () {
output.getSanitizingConverter = function() {
var converter = new Converter();
converter.hooks.chain("postConversion", sanitizeHtml);
converter.hooks.chain("postConversion", balanceTags);
converter.hooks.chain('postConversion', sanitizeHtml);
converter.hooks.chain('postConversion', balanceTags);
return converter;
}
};
function sanitizeHtml(html) {
return html.replace(/<[^>]*>?/gi, sanitizeTag);
@@ -31,21 +31,20 @@
if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white))
return tag;
else
return "";
return '';
}
/// <summary>
/// attempt to balance HTML tags in the html string
/// by removing any unmatched opening or closing tags
/// IMPORTANT: we *assume* HTML has *already* been
/// sanitized and is safe/sane before balancing!
///
/// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593
/// </summary>
// / <summary>
// / attempt to balance HTML tags in the html string
// / by removing any unmatched opening or closing tags
// / IMPORTANT: we *assume* HTML has *already* been
// / sanitized and is safe/sane before balancing!
// /
// / adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593
// / </summary>
function balanceTags(html) {
if (html == "")
return "";
if (html == '')
return '';
var re = /<\/?\w+[^>]*(\s|$|>)/g;
// convert everything to lower case; this makes
@@ -58,7 +57,7 @@
return html;
var tagname, tag;
var ignoredtags = "<p><img><br><li><hr>";
var ignoredtags = '<p><img><br><li><hr>';
var match;
var tagpaired = [];
var tagremove = [];
@@ -66,10 +65,10 @@
// loop through matched tags in forward order
for (var ctag = 0; ctag < tagcount; ctag++) {
tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1");
tagname = tags[ctag].replace(/<\/?(\w+).*/, '$1');
// skip any already paired tags
// and skip tags in our ignore list; assume they're self-closed
if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1)
if (tagpaired[ctag] || ignoredtags.search('<' + tagname + '>') > -1)
continue;
tag = tags[ctag];
@@ -79,7 +78,7 @@
// this is an opening tag
// search forwards (next tags), look for closing tags
for (var ntag = ctag + 1; ntag < tagcount; ntag++) {
if (!tagpaired[ntag] && tags[ntag] == "</" + tagname + ">") {
if (!tagpaired[ntag] && tags[ntag] == '</' + tagname + '>') {
match = ntag;
break;
}
@@ -98,8 +97,8 @@
// delete all orphaned tags from the string
var ctag = 0;
html = html.replace(re, function (match) {
var res = tagremove[ctag] ? "" : match;
html = html.replace(re, function(match) {
var res = tagremove[ctag] ? '' : match;
ctag++;
return res;
});

View File

@@ -1,15 +1,15 @@
$(document).ajaxError(function (event, jXHR) {
$(document).ajaxError(function(event, jXHR) {
if (jXHR.status === 403 && jXHR.responseText === 'Unauthenticated') {
var message = gettext(
'You have been logged out of your edX account. '+
'Click Okay to log in again now. '+
'Click Cancel to stay on this page '+
'You have been logged out of your edX account. ' +
'Click Okay to log in again now. ' +
'Click Cancel to stay on this page ' +
'(you must log in again to save your work).'
);
if (window.confirm(message)) {
var currentLocation = window.location.pathname;
window.location.href = '/login?next=' + encodeURIComponent(currentLocation);
};
}
}
});

View File

@@ -1,12 +1,12 @@
;(function (define) {
(function(define) {
'use strict';
define(['js/api_admin/views/catalog_preview'], function (CatalogPreviewView) {
return function (options) {
define(['js/api_admin/views/catalog_preview'], function(CatalogPreviewView) {
return function(options) {
var view = new CatalogPreviewView({
el: '.catalog-body',
previewUrl: options.previewUrl,
catalogApiUrl: options.catalogApiUrl,
catalogApiUrl: options.catalogApiUrl
});
return view.render();
};

View File

@@ -1,4 +1,4 @@
;(function(define) {
(function(define) {
'use strict';
define([
@@ -7,21 +7,21 @@
'gettext',
'text!../../../templates/api_admin/catalog-results.underscore',
'text!../../../templates/api_admin/catalog-error.underscore'
], function (Backbone, _, gettext, catalogResultsTpl, catalogErrorTpl) {
], function(Backbone, _, gettext, catalogResultsTpl, catalogErrorTpl) {
return Backbone.View.extend({
events: {
'click .preview-query': 'previewQuery'
},
initialize: function (options) {
initialize: function(options) {
this.previewUrl = options.previewUrl;
this.catalogApiUrl = options.catalogApiUrl;
},
render: function () {
render: function() {
this.$('#id_query').after(
'<button class="preview-query">'+ gettext('Preview this query') + '</button>'
'<button class="preview-query">' + gettext('Preview this query') + '</button>'
);
return this;
},
@@ -29,8 +29,8 @@
/*
* Return the user's query, URL-encoded.
*/
getQuery: function () {
return encodeURIComponent(this.$("#id_query").val());
getQuery: function() {
return encodeURIComponent(this.$('#id_query').val());
},
/*
@@ -38,12 +38,12 @@
* with the user's query. On success, displays the
* results, and on failure, displays an error message.
*/
previewQuery: function (event) {
previewQuery: function(event) {
event.preventDefault();
$.ajax(this.previewUrl + '?q=' + this.getQuery(), {
method: 'GET',
success: _.bind(this.renderCourses, this),
error: _.bind(function () {
error: _.bind(function() {
this.$('.preview-results').html(_.template(catalogErrorTpl)({}));
}, this)
});
@@ -53,12 +53,12 @@
* Render a list of courses with data returned by the
* courses API.
*/
renderCourses: function (data) {
renderCourses: function(data) {
this.$('.preview-results').html(_.template(catalogResultsTpl)({
'courses': data.results,
'catalogApiUrl': this.catalogApiUrl,
'catalogApiUrl': this.catalogApiUrl
}));
},
}
});
});
}).call(this, define || RequireJS.define);

View File

@@ -1,29 +1,28 @@
;(function (define) {
(function(define) {
'use strict';
define([
'backbone',
'edx-ui-toolkit/js/pagination/paging-collection',
'js/bookmarks/models/bookmark'
], function (Backbone, PagingCollection, BookmarkModel) {
return PagingCollection.extend({
model: BookmarkModel,
], function(Backbone, PagingCollection, BookmarkModel) {
return PagingCollection.extend({
model: BookmarkModel,
queryParams: {
course_id: function () { return this.options.course_id; },
fields : function () { return 'display_name,path'; }
},
queryParams: {
course_id: function() { return this.options.course_id; },
fields: function() { return 'display_name,path'; }
},
url: function() {
return this.url;
},
url: function() {
return this.url;
},
constructor: function (models, options) {
this.options = options;
this.url = options.url;
PagingCollection.prototype.constructor.call(this, models, options);
}
});
constructor: function(models, options) {
this.options = options;
this.url = options.url;
PagingCollection.prototype.constructor.call(this, models, options);
}
});
});
})(define || RequireJS.define);

View File

@@ -1,7 +1,6 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone'], function (Backbone) {
define(['backbone'], function(Backbone) {
return Backbone.Model.extend({
idAttribute: 'id',
defaults: {
@@ -12,10 +11,9 @@
created: ''
},
blockUrl: function () {
blockUrl: function() {
return '/courses/' + this.get('course_id') + '/jump_to/' + this.get('usage_id');
}
});
});
})(define || RequireJS.define);

View File

@@ -1,115 +1,114 @@
;(function (define, undefined) {
(function(define, undefined) {
'use strict';
define(['gettext', 'jquery', 'underscore', 'backbone', 'js/views/message_banner'],
function (gettext, $, _, Backbone, MessageBannerView) {
function(gettext, $, _, Backbone, MessageBannerView) {
return Backbone.View.extend({
errorMessage: gettext('An error has occurred. Please try again.'),
return Backbone.View.extend({
errorMessage: gettext('An error has occurred. Please try again.'),
srAddBookmarkText: gettext('Click to add'),
srRemoveBookmarkText: gettext('Click to remove'),
srAddBookmarkText: gettext('Click to add'),
srRemoveBookmarkText: gettext('Click to remove'),
events: {
'click': 'toggleBookmark'
},
events: {
'click': 'toggleBookmark'
},
showBannerInterval: 5000, // time in ms
showBannerInterval: 5000, // time in ms
initialize: function(options) {
this.apiUrl = options.apiUrl;
this.bookmarkId = options.bookmarkId;
this.bookmarked = options.bookmarked;
this.usageId = options.usageId;
this.setBookmarkState(this.bookmarked);
},
initialize: function (options) {
this.apiUrl = options.apiUrl;
this.bookmarkId = options.bookmarkId;
this.bookmarked = options.bookmarked;
this.usageId = options.usageId;
this.setBookmarkState(this.bookmarked);
},
toggleBookmark: function(event) {
event.preventDefault();
toggleBookmark: function(event) {
event.preventDefault();
this.$el.prop('disabled', true);
this.$el.prop('disabled', true);
if (this.$el.hasClass('bookmarked')) {
this.removeBookmark();
} else {
this.addBookmark();
}
},
if (this.$el.hasClass('bookmarked')) {
this.removeBookmark();
} else {
this.addBookmark();
}
},
addBookmark: function() {
var view = this;
$.ajax({
data: {usage_id: view.usageId},
type: "POST",
url: view.apiUrl,
dataType: 'json',
success: function () {
view.$el.trigger('bookmark:add');
view.setBookmarkState(true);
},
error: function (jqXHR) {
try {
var response = jqXHR.responseText ? JSON.parse(jqXHR.responseText) : '';
var userMessage = response ? response.user_message : '';
view.showError(userMessage);
}
catch(err) {
addBookmark: function() {
var view = this;
$.ajax({
data: {usage_id: view.usageId},
type: 'POST',
url: view.apiUrl,
dataType: 'json',
success: function() {
view.$el.trigger('bookmark:add');
view.setBookmarkState(true);
},
error: function(jqXHR) {
try {
var response = jqXHR.responseText ? JSON.parse(jqXHR.responseText) : '';
var userMessage = response ? response.user_message : '';
view.showError(userMessage);
}
catch (err) {
view.showError();
}
},
complete: function () {
view.$el.prop('disabled', false);
}
});
},
removeBookmark: function() {
var view = this;
var deleteUrl = view.apiUrl + view.bookmarkId + '/';
$.ajax({
type: "DELETE",
url: deleteUrl,
success: function () {
view.$el.trigger('bookmark:remove');
view.setBookmarkState(false);
},
error: function() {
view.showError();
},
complete: function() {
view.$el.prop('disabled', false);
}
});
},
setBookmarkState: function(bookmarked) {
if (bookmarked) {
this.$el.addClass('bookmarked');
this.$el.attr('aria-pressed', 'true');
this.$el.find('.bookmark-sr').text(this.srRemoveBookmarkText);
} else {
this.$el.removeClass('bookmarked');
this.$el.attr('aria-pressed', 'false');
this.$el.find('.bookmark-sr').text(this.srAddBookmarkText);
}
},
showError: function (errorText) {
var errorMsg = errorText || this.errorMessage;
if (!this.messageView) {
this.messageView = new MessageBannerView({
el: $('.message-banner'),
type: 'error'
},
complete: function() {
view.$el.prop('disabled', false);
}
});
}
this.messageView.showMessage(errorMsg);
},
removeBookmark: function() {
var view = this;
var deleteUrl = view.apiUrl + view.bookmarkId + '/';
$.ajax({
type: 'DELETE',
url: deleteUrl,
success: function() {
view.$el.trigger('bookmark:remove');
view.setBookmarkState(false);
},
error: function() {
view.showError();
},
complete: function() {
view.$el.prop('disabled', false);
}
});
},
setBookmarkState: function(bookmarked) {
if (bookmarked) {
this.$el.addClass('bookmarked');
this.$el.attr('aria-pressed', 'true');
this.$el.find('.bookmark-sr').text(this.srRemoveBookmarkText);
} else {
this.$el.removeClass('bookmarked');
this.$el.attr('aria-pressed', 'false');
this.$el.find('.bookmark-sr').text(this.srAddBookmarkText);
}
},
showError: function(errorText) {
var errorMsg = errorText || this.errorMessage;
if (!this.messageView) {
this.messageView = new MessageBannerView({
el: $('.message-banner'),
type: 'error'
});
}
this.messageView.showMessage(errorMsg);
// Hide message automatically after some interval
setTimeout(_.bind(function () {
this.messageView.hideMessage();
}, this), this.showBannerInterval);
}
setTimeout(_.bind(function() {
this.messageView.hideMessage();
}, this), this.showBannerInterval);
}
});
});
});
}).call(this, define || RequireJS.define);

View File

@@ -1,133 +1,132 @@
;(function (define, undefined) {
(function(define, undefined) {
'use strict';
define(['gettext', 'jquery', 'underscore', 'backbone', 'logger', 'moment', 'edx-ui-toolkit/js/utils/html-utils',
'common/js/components/views/paging_header', 'common/js/components/views/paging_footer',
'text!templates/bookmarks/bookmarks-list.underscore'
],
function (gettext, $, _, Backbone, Logger, _moment, HtmlUtils,
function(gettext, $, _, Backbone, Logger, _moment, HtmlUtils,
PagingHeaderView, PagingFooterView, BookmarksListTemplate) {
var moment = _moment || window.moment;
var moment = _moment || window.moment;
return Backbone.View.extend({
return Backbone.View.extend({
el: '.courseware-results',
coursewareContentEl: '#course-content',
coursewareResultsWrapperEl: '.courseware-results-wrapper',
el: '.courseware-results',
coursewareContentEl: '#course-content',
coursewareResultsWrapperEl: '.courseware-results-wrapper',
errorIcon: '<span class="fa fa-fw fa-exclamation-triangle message-error" aria-hidden="true"></span>',
loadingIcon: '<span class="fa fa-fw fa-spinner fa-pulse message-in-progress" aria-hidden="true"></span>',
errorIcon: '<span class="fa fa-fw fa-exclamation-triangle message-error" aria-hidden="true"></span>',
loadingIcon: '<span class="fa fa-fw fa-spinner fa-pulse message-in-progress" aria-hidden="true"></span>',
errorMessage: gettext('An error has occurred. Please try again.'),
loadingMessage: gettext('Loading'),
errorMessage: gettext('An error has occurred. Please try again.'),
loadingMessage: gettext('Loading'),
defaultPage: 1,
defaultPage: 1,
events: {
'click .bookmarks-results-list-item': 'visitBookmark'
},
events : {
'click .bookmarks-results-list-item': 'visitBookmark'
},
initialize: function(options) {
this.template = HtmlUtils.template(BookmarksListTemplate);
this.loadingMessageView = options.loadingMessageView;
this.errorMessageView = options.errorMessageView;
this.langCode = $(this.el).data('langCode');
this.pagingHeaderView = new PagingHeaderView({collection: this.collection});
this.pagingFooterView = new PagingFooterView({collection: this.collection});
this.listenTo(this.collection, 'page_changed', this.render);
_.bindAll(this, 'render', 'humanFriendlyDate');
},
initialize: function (options) {
this.template = HtmlUtils.template(BookmarksListTemplate);
this.loadingMessageView = options.loadingMessageView;
this.errorMessageView = options.errorMessageView;
this.langCode = $(this.el).data('langCode');
this.pagingHeaderView = new PagingHeaderView({collection: this.collection});
this.pagingFooterView = new PagingFooterView({collection: this.collection});
this.listenTo(this.collection, 'page_changed', this.render);
_.bindAll(this, 'render', 'humanFriendlyDate');
},
render: function() {
var data = {
bookmarksCollection: this.collection,
humanFriendlyDate: this.humanFriendlyDate
};
render: function () {
var data = {
bookmarksCollection: this.collection,
humanFriendlyDate: this.humanFriendlyDate
};
HtmlUtils.setHtml(this.$el, this.template(data));
this.pagingHeaderView.setElement(this.$('.paging-header')).render();
this.pagingFooterView.setElement(this.$('.paging-footer')).render();
this.delegateEvents();
return this;
},
HtmlUtils.setHtml(this.$el, this.template(data));
this.pagingHeaderView.setElement(this.$('.paging-header')).render();
this.pagingFooterView.setElement(this.$('.paging-footer')).render();
this.delegateEvents();
return this;
},
showBookmarks: function() {
var view = this;
showBookmarks: function () {
var view = this;
this.hideErrorMessage();
this.showBookmarksContainer();
this.hideErrorMessage();
this.showBookmarksContainer();
this.collection.getPage(this.defaultPage).done(function() {
view.render();
view.focusBookmarksElement();
}).fail(function() {
view.showErrorMessage();
});
},
this.collection.getPage(this.defaultPage).done(function () {
view.render();
view.focusBookmarksElement();
}).fail(function () {
view.showErrorMessage();
});
},
visitBookmark: function (event) {
var bookmarkedComponent = $(event.currentTarget);
var bookmark_id = bookmarkedComponent.data('bookmarkId');
var component_usage_id = bookmarkedComponent.data('usageId');
var component_type = bookmarkedComponent.data('componentType');
Logger.log(
visitBookmark: function(event) {
var bookmarkedComponent = $(event.currentTarget);
var bookmark_id = bookmarkedComponent.data('bookmarkId');
var component_usage_id = bookmarkedComponent.data('usageId');
var component_type = bookmarkedComponent.data('componentType');
Logger.log(
'edx.bookmark.accessed',
{
bookmark_id: bookmark_id,
component_type: component_type,
component_usage_id: component_usage_id
}
).always(function () {
{
bookmark_id: bookmark_id,
component_type: component_type,
component_usage_id: component_usage_id
}
).always(function() {
window.location.href = event.currentTarget.pathname;
});
},
},
/**
* Convert ISO 8601 formatted date into human friendly format. e.g, `2014-05-23T14:00:00Z` to `May 23, 2014`
* @param {String} isoDate - ISO 8601 formatted date string.
*/
humanFriendlyDate: function (isoDate) {
moment.locale(this.langCode);
return moment(isoDate).format('LL');
},
humanFriendlyDate: function(isoDate) {
moment.locale(this.langCode);
return moment(isoDate).format('LL');
},
areBookmarksVisible: function () {
return this.$('#my-bookmarks').is(":visible");
},
areBookmarksVisible: function() {
return this.$('#my-bookmarks').is(':visible');
},
hideBookmarks: function () {
this.$el.hide();
$(this.coursewareResultsWrapperEl).hide();
$(this.coursewareContentEl).css( 'display', 'table-cell');
},
hideBookmarks: function() {
this.$el.hide();
$(this.coursewareResultsWrapperEl).hide();
$(this.coursewareContentEl).css('display', 'table-cell');
},
showBookmarksContainer: function () {
$(this.coursewareContentEl).hide();
showBookmarksContainer: function() {
$(this.coursewareContentEl).hide();
// Empty el if it's not empty to get the clean state.
this.$el.html('');
this.$el.show();
$(this.coursewareResultsWrapperEl).css('display', 'table-cell');
},
this.$el.html('');
this.$el.show();
$(this.coursewareResultsWrapperEl).css('display', 'table-cell');
},
showLoadingMessage: function () {
this.loadingMessageView.showMessage(this.loadingMessage, this.loadingIcon);
},
showLoadingMessage: function() {
this.loadingMessageView.showMessage(this.loadingMessage, this.loadingIcon);
},
hideLoadingMessage: function () {
this.loadingMessageView.hideMessage();
},
hideLoadingMessage: function() {
this.loadingMessageView.hideMessage();
},
showErrorMessage: function () {
this.errorMessageView.showMessage(this.errorMessage, this.errorIcon);
},
showErrorMessage: function() {
this.errorMessageView.showMessage(this.errorMessage, this.errorIcon);
},
hideErrorMessage: function () {
this.errorMessageView.hideMessage();
},
hideErrorMessage: function() {
this.errorMessageView.hideMessage();
},
focusBookmarksElement: function () {
this.$('#my-bookmarks').focus();
}
focusBookmarksElement: function() {
this.$('#my-bookmarks').focus();
}
});
});
});
}).call(this, define || RequireJS.define);

View File

@@ -1,47 +1,46 @@
;(function (define, undefined) {
(function(define, undefined) {
'use strict';
define(['gettext', 'jquery', 'underscore', 'backbone', 'js/bookmarks/views/bookmarks_list',
'js/bookmarks/collections/bookmarks', 'js/views/message_banner'],
function (gettext, $, _, Backbone, BookmarksListView, BookmarksCollection, MessageBannerView) {
function(gettext, $, _, Backbone, BookmarksListView, BookmarksCollection, MessageBannerView) {
return Backbone.View.extend({
return Backbone.View.extend({
el: '.courseware-bookmarks-button',
el: '.courseware-bookmarks-button',
loadingMessageElement: '#loading-message',
errorMessageElement: '#error-message',
loadingMessageElement: '#loading-message',
errorMessageElement: '#error-message',
events: {
'click .bookmarks-list-button': 'toggleBookmarksListView'
},
events: {
'click .bookmarks-list-button': 'toggleBookmarksListView'
},
initialize: function () {
var bookmarksCollection = new BookmarksCollection([],
{
course_id: $('.courseware-results').data('courseId'),
url: $('.courseware-bookmarks-button').data('bookmarksApiUrl')
}
initialize: function() {
var bookmarksCollection = new BookmarksCollection([],
{
course_id: $('.courseware-results').data('courseId'),
url: $('.courseware-bookmarks-button').data('bookmarksApiUrl')
}
);
this.bookmarksListView = new BookmarksListView(
{
collection: bookmarksCollection,
loadingMessageView: new MessageBannerView({el: $(this.loadingMessageElement)}),
errorMessageView: new MessageBannerView({el: $(this.errorMessageElement)})
}
this.bookmarksListView = new BookmarksListView(
{
collection: bookmarksCollection,
loadingMessageView: new MessageBannerView({el: $(this.loadingMessageElement)}),
errorMessageView: new MessageBannerView({el: $(this.errorMessageElement)})
}
);
},
},
toggleBookmarksListView: function () {
if (this.bookmarksListView.areBookmarksVisible()) {
this.bookmarksListView.hideBookmarks();
this.$('.bookmarks-list-button').attr('aria-pressed', 'false');
this.$('.bookmarks-list-button').removeClass('is-active').addClass('is-inactive');
} else {
this.bookmarksListView.showBookmarks();
this.$('.bookmarks-list-button').attr('aria-pressed', 'true');
this.$('.bookmarks-list-button').removeClass('is-inactive').addClass('is-active');
toggleBookmarksListView: function() {
if (this.bookmarksListView.areBookmarksVisible()) {
this.bookmarksListView.hideBookmarks();
this.$('.bookmarks-list-button').attr('aria-pressed', 'false');
this.$('.bookmarks-list-button').removeClass('is-active').addClass('is-inactive');
} else {
this.bookmarksListView.showBookmarks();
this.$('.bookmarks-list-button').attr('aria-pressed', 'true');
this.$('.bookmarks-list-button').removeClass('is-inactive').addClass('is-active');
}
}
}
});
});
});
}).call(this, define || RequireJS.define);

View File

@@ -1,535 +1,535 @@
var edx = edx || {};
(function($, _, Backbone, gettext) {
'use strict';
'use strict';
edx.ccx = edx.ccx || {};
edx.ccx.schedule = edx.ccx.schedule || {};
var self;
edx.ccx = edx.ccx || {};
edx.ccx.schedule = edx.ccx.schedule || {};
var self;
edx.ccx.schedule.reloadPage = function() {
location.reload();
};
edx.ccx.schedule.reloadPage = function() {
location.reload();
};
edx.ccx.schedule.UnitModel = Backbone.Model.extend({
defaults: {
location: '',
display_name: '',
start: null,
due: null,
category: '',
hidden: false,
children: []
}
});
edx.ccx.schedule.UnitModel = Backbone.Model.extend({
defaults: {
location: '',
display_name: '',
start: null,
due: null,
category: '',
hidden: false,
children: []
}
});
edx.ccx.schedule.Schedule = Backbone.Collection.extend({
model: edx.ccx.schedule.UnitModel,
url: 'ccx_schedule'
});
edx.ccx.schedule.Schedule = Backbone.Collection.extend({
model: edx.ccx.schedule.UnitModel,
url: 'ccx_schedule'
});
edx.ccx.schedule.ScheduleView = Backbone.View.extend({
edx.ccx.schedule.ScheduleView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.schedule_collection = new edx.ccx.schedule.Schedule();
this.schedule = {};
this.schedule_collection.bind('reset', this.render);
this.schedule_collection.fetch({reset: true});
this.chapter_select = $('form#add-unit select[name="chapter"]');
this.sequential_select = $('form#add-unit select[name="sequential"]');
this.vertical_select = $('form#add-unit select[name="vertical"]');
this.dirty = false;
self = this;
$('#add-all').on('click', function(event) {
event.preventDefault();
self.schedule_apply(self.schedule, self.show);
self.dirty = true;
self.schedule_collection.set(self.schedule);
self.render();
});
initialize: function() {
_.bindAll(this, 'render');
this.schedule_collection = new edx.ccx.schedule.Schedule();
this.schedule = {};
this.schedule_collection.bind('reset', this.render);
this.schedule_collection.fetch({reset: true});
this.chapter_select = $('form#add-unit select[name="chapter"]');
this.sequential_select = $('form#add-unit select[name="sequential"]');
this.vertical_select = $('form#add-unit select[name="vertical"]');
this.dirty = false;
self = this;
$('#add-all').on('click', function(event) {
event.preventDefault();
self.schedule_apply(self.schedule, self.show);
self.dirty = true;
self.schedule_collection.set(self.schedule);
self.render();
});
// By default input date and time fileds are disable.
self.disableFields($('.ccx_due_date_time_fields'));
self.disableFields($('.ccx_start_date_time_fields'));
self.disableFields($('.ccx_due_date_time_fields'));
self.disableFields($('.ccx_start_date_time_fields'));
// Add unit handlers
this.chapter_select.on('change', function() {
var chapter_location = self.chapter_select.val();
self.vertical_select.html('').prop('disabled', true);
if (chapter_location !== 'none') {
var chapter = self.find_unit(self.hidden, chapter_location);
self.sequential_select.html('')
.append('<option value="all">'+gettext("All subsections")+'</option>')
this.chapter_select.on('change', function() {
var chapter_location = self.chapter_select.val();
self.vertical_select.html('').prop('disabled', true);
if (chapter_location !== 'none') {
var chapter = self.find_unit(self.hidden, chapter_location);
self.sequential_select.html('')
.append('<option value="all">' + gettext('All subsections') + '</option>')
.append(self.schedule_options(chapter.children));
self.sequential_select.prop('disabled', false);
$('#add-unit-button').prop('disabled', false);
self.sequential_select.prop('disabled', false);
$('#add-unit-button').prop('disabled', false);
// When a chapter is selected, start date fields are enabled and due date
// fields are disabled because due dates are not applicable on a chapter.
self.disableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
} else {
self.sequential_select.html('').prop('disabled', true);
self.disableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
} else {
self.sequential_select.html('').prop('disabled', true);
// When no chapter is selected, all date fields are disabled.
self.disableFields($('.ccx_due_date_time_fields'));
self.disableFields($('.ccx_start_date_time_fields'));
}
});
self.disableFields($('.ccx_due_date_time_fields'));
self.disableFields($('.ccx_start_date_time_fields'));
}
});
this.sequential_select.on('change', function() {
var sequential_location = self.sequential_select.val();
if (sequential_location !== 'all') {
var chapter = self.chapter_select.val(),
sequential = self.find_unit(self.hidden, chapter, sequential_location);
self.vertical_select.html('')
.append('<option value="all">'+gettext("All units")+'</option>')
this.sequential_select.on('change', function() {
var sequential_location = self.sequential_select.val();
if (sequential_location !== 'all') {
var chapter = self.chapter_select.val(),
sequential = self.find_unit(self.hidden, chapter, sequential_location);
self.vertical_select.html('')
.append('<option value="all">' + gettext('All units') + '</option>')
.append(self.schedule_options(sequential.children));
self.vertical_select.prop('disabled', false);
self.set_datetime('start', sequential.start);
self.set_datetime('due', sequential.due);
self.vertical_select.prop('disabled', false);
self.set_datetime('start', sequential.start);
self.set_datetime('due', sequential.due);
// When a subsection (aka sequential) is selected,
// both start and due date fields are enabled.
self.enableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
} else {
self.enableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
} else {
// When "All subsections" is selected, all date fields are disabled.
self.vertical_select.html('').prop('disabled', true);
self.disableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
}
});
self.vertical_select.html('').prop('disabled', true);
self.disableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
}
});
this.vertical_select.on('change', function() {
var vertical_location = self.vertical_select.val();
if (vertical_location !== 'all') {
var chapter = self.chapter_select.val(),
sequential = self.sequential_select.val();
var vertical = self.find_unit(
this.vertical_select.on('change', function() {
var vertical_location = self.vertical_select.val();
if (vertical_location !== 'all') {
var chapter = self.chapter_select.val(),
sequential = self.sequential_select.val();
var vertical = self.find_unit(
self.hidden, chapter, sequential, vertical_location);
// When a unit (aka vertical) is selected, all date fields are disabled because units
// inherit dates from subsection
self.disableFields($('.ccx_due_date_time_fields'));
self.disableFields($('.ccx_start_date_time_fields'));
} else {
self.disableFields($('.ccx_due_date_time_fields'));
self.disableFields($('.ccx_start_date_time_fields'));
} else {
// When "All units" is selected, all date fields are enabled,
// because units inherit dates from subsections and we
// are showing dates from the selected subsection.
self.enableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
}
});
self.enableFields($('.ccx_due_date_time_fields'));
self.enableFields($('.ccx_start_date_time_fields'));
}
});
// Add unit handler
$('#add-unit-button').on('click', function(event) {
event.preventDefault();
$('#add-unit-button').on('click', function(event) {
event.preventDefault();
// Default value of time is 00:00.
var start, chapter, sequential, vertical, units, due;
start = self.get_datetime('start');
chapter = self.chapter_select.val();
sequential = self.sequential_select.val();
vertical = self.vertical_select.val();
units = self.find_lineage(
var start, chapter, sequential, vertical, units, due;
start = self.get_datetime('start');
chapter = self.chapter_select.val();
sequential = self.sequential_select.val();
vertical = self.vertical_select.val();
units = self.find_lineage(
self.schedule,
chapter,
sequential === 'all' ? null : sequential,
vertical === 'all' ? null : vertical
);
due = self.get_datetime('due');
var errorMessage = self.valid_dates(start, due);
if (_.isUndefined(errorMessage)) {
units.map(self.show);
var unit = units[units.length - 1];
if (!_.isUndefined(unit)) {
if (!_.isNull(start)) {
unit.start = start;
}
if (!_.isNull(due)) {
unit.due = due;
}
}
self.schedule_apply([unit], self.show);
self.schedule_collection.set(self.schedule);
self.dirty = true;
self.render();
} else {
self.dirty = false;
$('#ccx_schedule_error_message').text(errorMessage);
$('#ajax-error').show().focus();
$('#dirty-schedule').hide();
}
});
due = self.get_datetime('due');
var errorMessage = self.valid_dates(start, due);
if (_.isUndefined(errorMessage)) {
units.map(self.show);
var unit = units[units.length - 1];
if (!_.isUndefined(unit)) {
if (!_.isNull(start)) {
unit.start = start;
}
if (!_.isNull(due)) {
unit.due = due;
}
}
self.schedule_apply([unit], self.show);
self.schedule_collection.set(self.schedule);
self.dirty = true;
self.render();
} else {
self.dirty = false;
$('#ccx_schedule_error_message').text(errorMessage);
$('#ajax-error').show().focus();
$('#dirty-schedule').hide();
}
});
// Handle save button
$('#dirty-schedule #save-changes').on('click', function(event) {
event.preventDefault();
self.save();
});
}, // end initialization
$('#dirty-schedule #save-changes').on('click', function(event) {
event.preventDefault();
self.save();
});
}, // end initialization
render: function() {
self.schedule = this.schedule_collection.toJSON();
self.hidden = this.pruned(self.schedule, function(node) {
return node.hidden || node.category !== 'vertical';
});
this.showing = this.pruned(self.schedule, function(node) {
return !node.hidden;
});
render: function() {
self.schedule = this.schedule_collection.toJSON();
self.hidden = this.pruned(self.schedule, function(node) {
return node.hidden || node.category !== 'vertical';
});
this.showing = this.pruned(self.schedule, function(node) {
return !node.hidden;
});
// schedule_template defined globally in ccx\schedule.html
/* globals schedule_template */
this.$el.html(schedule_template({chapters: this.showing}));
$('table.ccx-schedule .sequential,.vertical').hide();
$('table.ccx-schedule .unit .toggle-collapse').on('click', this.toggle_collapse);
this.$el.html(schedule_template({chapters: this.showing}));
$('table.ccx-schedule .sequential,.vertical').hide();
$('table.ccx-schedule .unit .toggle-collapse').on('click', this.toggle_collapse);
// Hidden hover fields for empty date fields
$('table.ccx-schedule .date button').each(function() {
if ($(this).text().trim() === gettext("Click to change")) {
$(this).html('Set date <span class="sr"> ' +
gettext("Click to change") + '</span>');
}
});
$('table.ccx-schedule .date button').each(function() {
if ($(this).text().trim() === gettext('Click to change')) {
$(this).html('Set date <span class="sr"> ' +
gettext('Click to change') + '</span>');
}
});
// Handle date edit clicks
$('table.ccx-schedule .date button').attr('href', '#enter-date-modal')
$('table.ccx-schedule .date button').attr('href', '#enter-date-modal')
.leanModal({closeButton: '.close-modal'});
$('table.ccx-schedule .due-date button').on('click', this.enterNewDate('due'));
$('table.ccx-schedule .start-date button').on('click', this.enterNewDate('start'));
$('table.ccx-schedule .due-date button').on('click', this.enterNewDate('due'));
$('table.ccx-schedule .start-date button').on('click', this.enterNewDate('start'));
// click handler for expand all
$('#ccx_expand_all_btn').on('click', self.expandAll);
$('#ccx_expand_all_btn').on('click', self.expandAll);
// click handler for collapse all
$('#ccx_collapse_all_btn').on('click', self.collapseAll);
$('#ccx_collapse_all_btn').on('click', self.collapseAll);
// Click handler for remove all
$('table.ccx-schedule button#remove-all').on('click', function(event) {
event.preventDefault();
self.schedule_apply(self.schedule, self.hide);
self.dirty = true;
self.schedule_collection.set(self.schedule);
self.render();
});
$('table.ccx-schedule button#remove-all').on('click', function(event) {
event.preventDefault();
self.schedule_apply(self.schedule, self.hide);
self.dirty = true;
self.schedule_collection.set(self.schedule);
self.render();
});
// Remove unit handler
$('table.ccx-schedule button.remove-unit').on('click', function() {
var row = $(this).closest('tr'),
path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]);
self.schedule_apply([unit], self.hide);
self.schedule_collection.set(self.schedule);
self.dirty = true;
self.render();
});
$('table.ccx-schedule button.remove-unit').on('click', function() {
var row = $(this).closest('tr'),
path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]);
self.schedule_apply([unit], self.hide);
self.schedule_collection.set(self.schedule);
self.dirty = true;
self.render();
});
// Show or hide form
if (this.hidden.length) {
if (this.hidden.length) {
// Populate chapters select, depopulate others
this.chapter_select.html('')
.append('<option value="none">'+gettext("Select a chapter")+'...</option>')
this.chapter_select.html('')
.append('<option value="none">' + gettext('Select a chapter') + '...</option>')
.append(self.schedule_options(this.hidden));
this.sequential_select.html('').prop('disabled', true);
this.vertical_select.html('').prop('disabled', true);
$('form#add-unit').show();
$('#all-units-added').hide();
$('#add-unit-button').prop('disabled', true);
} else {
$('form#add-unit').hide();
$('#all-units-added').show();
}
this.sequential_select.html('').prop('disabled', true);
this.vertical_select.html('').prop('disabled', true);
$('form#add-unit').show();
$('#all-units-added').hide();
$('#add-unit-button').prop('disabled', true);
} else {
$('form#add-unit').hide();
$('#all-units-added').show();
}
// Show or hide save button
if (this.dirty) {
$('#dirty-schedule').show();
$('html, body').animate(
{ scrollTop: $('#dirty-schedule').offset().top },
'slow', function() {$('#dirty-schedule').focus();
});
} else {
$('#dirty-schedule').hide();
}
$('#ajax-error').hide();
if (this.dirty) {
$('#dirty-schedule').show();
$('html, body').animate(
{scrollTop: $('#dirty-schedule').offset().top},
'slow', function() { $('#dirty-schedule').focus();
});
} else {
$('#dirty-schedule').hide();
}
$('#ajax-error').hide();
return this;
}, // end render
return this;
}, // end render
save: function() {
self.schedule_collection.set(self.schedule);
var button = $('#dirty-schedule #save-changes');
button.prop('disabled', true).text(gettext("Saving"));
save: function() {
self.schedule_collection.set(self.schedule);
var button = $('#dirty-schedule #save-changes');
button.prop('disabled', true).text(gettext('Saving'));
// save_url defined globally in ccx\schedule.html
/* globals save_url */
$.ajax({
url: save_url,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(self.schedule),
success: function(data) {
self.dirty = false;
self.render();
button.prop('disabled', false).text(gettext("Save changes"));
$.ajax({
url: save_url,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(self.schedule),
success: function(data) {
self.dirty = false;
self.render();
button.prop('disabled', false).text(gettext('Save changes'));
// Update textarea with grading policy JSON, since grading policy
// may have changed.
$('#grading-policy').text(data.grading_policy);
$('#grading-policy').text(data.grading_policy);
},
error: function(jqXHR) {
console.log(jqXHR.responseText);
$('#ajax-error').show().focus();
$('#dirty-schedule').hide();
$('form#add-unit select,input,button').prop('disabled', true);
button.prop('disabled', false).text(gettext('Save changes'));
}
});
}, // end save
hide: function(unit) {
if (unit !== undefined) {
unit.hidden = true;
}
},
error: function(jqXHR) {
console.log(jqXHR.responseText);
$('#ajax-error').show().focus();
$('#dirty-schedule').hide();
$('form#add-unit select,input,button').prop('disabled', true);
button.prop('disabled', false).text(gettext("Save changes"));
}
});
}, // end save
hide: function(unit) {
if (unit !== undefined) {
unit.hidden = true;
}
},
show: function(unit) {
if (unit !== undefined) {
unit.hidden = false;
}
},
show: function(unit) {
if (unit !== undefined) {
unit.hidden = false;
}
},
valid_dates: function(start, due) {
var errorMessage;
valid_dates: function(start, due) {
var errorMessage;
// Start date is compulsory and due date is optional.
if (_.isEmpty(start) && !_.isEmpty(due)) {
errorMessage = gettext("Please enter valid start date and time.");
} else if (!_.isEmpty(start) && !_.isEmpty(due)) {
var requirejs = window.require || RequireJS.require;
var moment = requirejs("moment");
var parsedDueDate = moment(due, 'YYYY-MM-DD HH:mm');
var parsedStartDate = moment(start, 'YYYY-MM-DD HH:mm');
if (parsedDueDate.isBefore(parsedStartDate)) {
errorMessage = gettext("Due date cannot be before start date.");
}
}
return errorMessage;
},
if (_.isEmpty(start) && !_.isEmpty(due)) {
errorMessage = gettext('Please enter valid start date and time.');
} else if (!_.isEmpty(start) && !_.isEmpty(due)) {
var requirejs = window.require || RequireJS.require;
var moment = requirejs('moment');
var parsedDueDate = moment(due, 'YYYY-MM-DD HH:mm');
var parsedStartDate = moment(start, 'YYYY-MM-DD HH:mm');
if (parsedDueDate.isBefore(parsedStartDate)) {
errorMessage = gettext('Due date cannot be before start date.');
}
}
return errorMessage;
},
get_datetime: function(which) {
var date = $('form#add-unit input[name=' + which + '_date]').val();
var time = $('form#add-unit input[name=' + which + '_time]').val();
time = _.isEmpty(time) ? "00:00" : time;
if (date && time) {
return date + ' ' + time;
}
return null;
},
get_datetime: function(which) {
var date = $('form#add-unit input[name=' + which + '_date]').val();
var time = $('form#add-unit input[name=' + which + '_time]').val();
time = _.isEmpty(time) ? '00:00' : time;
if (date && time) {
return date + ' ' + time;
}
return null;
},
set_datetime: function(which, value) {
var parts = value ? value.split(' ') : ['', ''],
date = parts[0],
time = parts[1];
$('form#add-unit input[name=' + which + '_date]').val(date);
$('form#add-unit input[name=' + which + '_time]').val(time);
},
set_datetime: function(which, value) {
var parts = value ? value.split(' ') : ['', ''],
date = parts[0],
time = parts[1];
$('form#add-unit input[name=' + which + '_date]').val(date);
$('form#add-unit input[name=' + which + '_time]').val(time);
},
schedule_options: function(nodes) {
return nodes.map(function(node) {
return $('<option>')
schedule_options: function(nodes) {
return nodes.map(function(node) {
return $('<option>')
.attr('value', node.location)
.text(node.display_name)[0];
});
},
});
},
schedule_apply: function(nodes, f) {
nodes.map(function(node) {
f(node);
if (node !== undefined && node.children !== undefined) {
self.schedule_apply(node.children, f);
}
});
},
schedule_apply: function(nodes, f) {
nodes.map(function(node) {
f(node);
if (node !== undefined && node.children !== undefined) {
self.schedule_apply(node.children, f);
}
});
},
pruned: function(tree, filter) {
return tree.filter(filter)
pruned: function(tree, filter) {
return tree.filter(filter)
.map(function(node) {
var copy = {};
$.extend(copy, node);
if (node.children) {
copy.children = self.pruned(node.children, filter);
}
return copy;
}).filter(function(node) {
return node.children === undefined || node.children.length;
});
},
disableFields: function($selector) {
$selector.find('select,input,button').prop('disabled', true);
},
enableFields: function($selector) {
$selector.find('select,input,button').prop('disabled', false);
},
toggle_collapse: function(event) {
event.preventDefault();
var row = $(this).closest('tr');
var children = self.get_children(row);
if (row.is('.expanded')) {
$(this).attr('aria-expanded', 'false');
$(this).find(".fa-caret-down").removeClass('fa-caret-down').addClass('fa-caret-right');
row.removeClass('expanded').addClass('collapsed');
children.hide();
} else {
$(this).attr('aria-expanded', 'true');
$(this).find(".fa-caret-right").removeClass('fa-caret-right').addClass('fa-caret-down');
row.removeClass('collapsed').addClass('expanded');
var depth = $(row).data('depth');
var $childNodes = children.filter('.collapsed');
if ($childNodes.length <= 0) {
children.show();
} else {
// this will expand units.
$childNodes.each(function() {
var depthChild = $(this).data('depth');
if (depth === (depthChild - 1)) {
$(this).show();
var copy = {};
$.extend(copy, node);
if (node.children) {
copy.children = self.pruned(node.children, filter);
}
});
}
}
},
return copy;
}).filter(function(node) {
return node.children === undefined || node.children.length;
});
},
expandAll : function() {
$('table.ccx-schedule > tbody > tr').each(function() {
var row = $(this);
if (!row.is('.expanded')) {
var children = self.get_children(row);
row.find(".ccx_sr_alert").attr("aria-expanded", "true");
row.find(".fa-caret-right").removeClass('fa-caret-right').addClass('fa-caret-down');
row.removeClass('collapsed').addClass('expanded');
children.filter('.collapsed').each(function() {
children = children.not(self.get_children(this));
});
children.show();
}
});
},
disableFields: function($selector) {
$selector.find('select,input,button').prop('disabled', true);
},
collapseAll: function() {
$('table.ccx-schedule > tbody > tr').each(function() {
var row = $(this);
if (row.is('.expanded')) {
$(row).find('.ccx_sr_alert').attr('aria-expanded', 'false');
$(row).find('.fa-caret-down').removeClass('fa-caret-down').addClass('fa-caret-right');
row.removeClass('expanded').addClass('collapsed');
}
});
$('table.ccx-schedule .sequential,.vertical').hide();
},
enableFields: function($selector) {
$selector.find('select,input,button').prop('disabled', false);
},
enterNewDate: function(what) {
return function() {
var row = $(this).closest('tr');
var modal = $('#enter-date-modal')
toggle_collapse: function(event) {
event.preventDefault();
var row = $(this).closest('tr');
var children = self.get_children(row);
if (row.is('.expanded')) {
$(this).attr('aria-expanded', 'false');
$(this).find('.fa-caret-down').removeClass('fa-caret-down').addClass('fa-caret-right');
row.removeClass('expanded').addClass('collapsed');
children.hide();
} else {
$(this).attr('aria-expanded', 'true');
$(this).find('.fa-caret-right').removeClass('fa-caret-right').addClass('fa-caret-down');
row.removeClass('collapsed').addClass('expanded');
var depth = $(row).data('depth');
var $childNodes = children.filter('.collapsed');
if ($childNodes.length <= 0) {
children.show();
} else {
// this will expand units.
$childNodes.each(function() {
var depthChild = $(this).data('depth');
if (depth === (depthChild - 1)) {
$(this).show();
}
});
}
}
},
expandAll: function() {
$('table.ccx-schedule > tbody > tr').each(function() {
var row = $(this);
if (!row.is('.expanded')) {
var children = self.get_children(row);
row.find('.ccx_sr_alert').attr('aria-expanded', 'true');
row.find('.fa-caret-right').removeClass('fa-caret-right').addClass('fa-caret-down');
row.removeClass('collapsed').addClass('expanded');
children.filter('.collapsed').each(function() {
children = children.not(self.get_children(this));
});
children.show();
}
});
},
collapseAll: function() {
$('table.ccx-schedule > tbody > tr').each(function() {
var row = $(this);
if (row.is('.expanded')) {
$(row).find('.ccx_sr_alert').attr('aria-expanded', 'false');
$(row).find('.fa-caret-down').removeClass('fa-caret-down').addClass('fa-caret-right');
row.removeClass('expanded').addClass('collapsed');
}
});
$('table.ccx-schedule .sequential,.vertical').hide();
},
enterNewDate: function(what) {
return function() {
var row = $(this).closest('tr');
var modal = $('#enter-date-modal')
.data('what', what)
.data('location', row.data('location'));
modal.find('h2').text(
what === 'due' ? gettext("Enter Due Date and Time") :
gettext("Enter Start Date and Time")
modal.find('h2').text(
what === 'due' ? gettext('Enter Due Date and Time') :
gettext('Enter Start Date and Time')
);
modal.focus();
$(document).on('focusin', function(event) {
try {
if (!_.isUndefined(event.target.closest('.modal').id) &&
modal.focus();
$(document).on('focusin', function(event) {
try {
if (!_.isUndefined(event.target.closest('.modal').id) &&
event.target.closest('.modal').id !== 'enter-date-modal' &&
event.target.id !== 'enter-date-modal') {
event.preventDefault();
modal.find('.close-modal').focus();
}
} catch (err) {
event.preventDefault();
modal.find('.close-modal').focus();
}
});
modal.find('.close-modal').click(function () {
$(document).off('focusin');
});
var path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]),
parts = unit[what] ? unit[what].split(' ') : ['', ''],
date = parts[0],
time = parts[1];
modal.find('input[name=date]').val(date);
modal.find('input[name=time]').val(time);
modal.find('form').off('submit').on('submit', function(event) {
event.preventDefault();
var date = $(this).find('input[name=date]').val(),
time = $(this).find('input[name=time]').val();
var valid_date = new Date(date);
if (isNaN(valid_date.valueOf())) {
alert('Please enter a valid date');
return;
}
var valid_time = /^\d{1,2}:\d{2}?$/;
if (!time.match(valid_time)) {
alert('Please enter a valid time');
return;
}
if (what === 'start') {
unit.start = date + ' ' + time;
if (unit.category === "sequential") {
self.updateChildrenDates(unit, what, unit.start);
}
} else {
unit.due = date + ' ' + time;
if (unit.category === "sequential") {
self.updateChildrenDates(unit, what, unit.due);
}
}
modal.find('.close-modal').click();
self.dirty = true;
self.schedule_collection.set(self.schedule);
self.render();
});
};
},
event.preventDefault();
modal.find('.close-modal').focus();
}
} catch (err) {
event.preventDefault();
modal.find('.close-modal').focus();
}
});
modal.find('.close-modal').click(function() {
$(document).off('focusin');
});
var path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]),
parts = unit[what] ? unit[what].split(' ') : ['', ''],
date = parts[0],
time = parts[1];
modal.find('input[name=date]').val(date);
modal.find('input[name=time]').val(time);
modal.find('form').off('submit').on('submit', function(event) {
event.preventDefault();
var date = $(this).find('input[name=date]').val(),
time = $(this).find('input[name=time]').val();
var valid_date = new Date(date);
if (isNaN(valid_date.valueOf())) {
alert('Please enter a valid date');
return;
}
var valid_time = /^\d{1,2}:\d{2}?$/;
if (!time.match(valid_time)) {
alert('Please enter a valid time');
return;
}
if (what === 'start') {
unit.start = date + ' ' + time;
if (unit.category === 'sequential') {
self.updateChildrenDates(unit, what, unit.start);
}
} else {
unit.due = date + ' ' + time;
if (unit.category === 'sequential') {
self.updateChildrenDates(unit, what, unit.due);
}
}
modal.find('.close-modal').click();
self.dirty = true;
self.schedule_collection.set(self.schedule);
self.render();
});
};
},
updateChildrenDates: function(sequential, date_type, date) {
updateChildrenDates: function(sequential, date_type, date) {
// This code iterates the children (aka verticals) of a sequential.
// It updates start and due dates to corresponding dates
// of sequential (parent).
_.forEach(sequential.children, function (unit) {
if (date_type === 'start') {
unit.start = date;
} else {
unit.due = date;
}
});
},
_.forEach(sequential.children, function(unit) {
if (date_type === 'start') {
unit.start = date;
} else {
unit.due = date;
}
});
},
find_unit: function(tree, chapter, sequential, vertical) {
var units = self.find_lineage(tree, chapter, sequential, vertical);
return units[units.length -1];
},
find_unit: function(tree, chapter, sequential, vertical) {
var units = self.find_lineage(tree, chapter, sequential, vertical);
return units[units.length - 1];
},
find_lineage: function(tree, chapter, sequential, vertical) {
function find_in(seq, location) {
for (var i = 0; i < seq.length; i++) {
if (seq[i].location === location) {
return seq[i];
}
}
}
var units = [],
unit = find_in(tree, chapter);
units[units.length] = unit;
if (sequential) {
units[units.length] = unit = find_in(unit.children, sequential);
if (vertical) {
units[units.length] = unit = find_in(unit.children, vertical);
}
}
return units;
},
get_children: function(row) {
var depth = $(row).data('depth');
return $(row).nextUntil(
find_lineage: function(tree, chapter, sequential, vertical) {
function find_in(seq, location) {
for (var i = 0; i < seq.length; i++) {
if (seq[i].location === location) {
return seq[i];
}
}
}
var units = [],
unit = find_in(tree, chapter);
units[units.length] = unit;
if (sequential) {
units[units.length] = unit = find_in(unit.children, sequential);
if (vertical) {
units[units.length] = unit = find_in(unit.children, vertical);
}
}
return units;
},
get_children: function(row) {
var depth = $(row).data('depth');
return $(row).nextUntil(
$(row).siblings().filter(function() {
return $(this).data('depth') <= depth;
return $(this).data('depth') <= depth;
})
);
}
});
}
});
})(jQuery, _, Backbone, gettext);

View File

@@ -1,10 +1,10 @@
$(function () {
$(function() {
'use strict';
$('.action-share-mozillaopenbadges').click(function (event) {
$('.action-share-mozillaopenbadges').click(function(event) {
$('.badges-overlay').fadeIn();
event.preventDefault();
});
$('.badges-modal .close').click(function () {
$('.badges-modal .close').click(function() {
$('.badges-overlay').fadeOut();
});
});
});

View File

@@ -1,7 +1,7 @@
// Backbone.js Application Collection: CertificateInvalidationCollection
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define) {
(function(define) {
'use strict';
define(
@@ -17,4 +17,4 @@
});
}
);
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

View File

@@ -1,42 +1,41 @@
// Backbone.js Application Collection: CertificateWhiteList
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define){
(function(define) {
'use strict';
define([
'backbone',
'gettext',
'js/certificates/models/certificate_exception'
],
'backbone',
'gettext',
'js/certificates/models/certificate_exception'
],
function(Backbone, gettext, CertificateExceptionModel){
var CertificateWhiteList = Backbone.Collection.extend({
function(Backbone, gettext, CertificateExceptionModel) {
var CertificateWhiteList = Backbone.Collection.extend({
model: CertificateExceptionModel,
initialize: function(attrs, options){
initialize: function(attrs, options) {
this.url = options.url;
this.generate_certificates_url = options.generate_certificates_url;
},
getModel: function(attrs){
getModel: function(attrs) {
var model = this.findWhere({user_name: attrs.user_name});
if(attrs.user_name && model){
if (attrs.user_name && model) {
return model;
}
model = this.findWhere({user_email: attrs.user_email});
if(attrs.user_email && model){
if (attrs.user_email && model) {
return model;
}
return undefined;
},
sync: function(options, appended_url){
sync: function(options, appended_url) {
var filtered = [];
if(appended_url === 'new'){
filtered = this.filter(function(model){
if (appended_url === 'new') {
filtered = this.filter(function(model) {
return model.get('new');
});
}
@@ -48,8 +47,8 @@
);
},
update: function(data){
_.each(data, function(item){
update: function(data) {
_.each(data, function(item) {
var certificate_exception_model =
this.getModel({user_name: item.user_name, user_email: item.user_email});
certificate_exception_model.set(item);
@@ -60,4 +59,4 @@
return CertificateWhiteList;
}
);
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

View File

@@ -1,7 +1,7 @@
// Backbone.js Page Object Factory: Certificate Invalidation Factory
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define) {
(function(define) {
'use strict';
define(
[
@@ -9,7 +9,6 @@
'js/certificates/collections/certificate_invalidation_collection'
],
function(CertificateInvalidationView, CertificateInvalidationCollection) {
return function(certificate_invalidation_collection_json, certificate_invalidation_url) {
var certificate_invalidation_collection = new CertificateInvalidationCollection(
JSON.parse(certificate_invalidation_collection_json), {
@@ -25,7 +24,6 @@
certificate_invalidation_view.render();
};
}
);
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

View File

@@ -1,22 +1,21 @@
// Backbone.js Page Object Factory: Certificates
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define){
(function(define) {
'use strict';
define([
'jquery',
'js/certificates/views/certificate_whitelist',
'js/certificates/models/certificate_exception',
'js/certificates/views/certificate_whitelist_editor',
'js/certificates/collections/certificate_whitelist',
'js/certificates/views/certificate_bulk_whitelist'
],
function($, CertificateWhiteListListView, CertificateExceptionModel, CertificateWhiteListEditorView ,
CertificateWhiteListCollection, CertificateBulkWhiteList){
'jquery',
'js/certificates/views/certificate_whitelist',
'js/certificates/models/certificate_exception',
'js/certificates/views/certificate_whitelist_editor',
'js/certificates/collections/certificate_whitelist',
'js/certificates/views/certificate_bulk_whitelist'
],
function($, CertificateWhiteListListView, CertificateExceptionModel, CertificateWhiteListEditorView,
CertificateWhiteListCollection, CertificateBulkWhiteList) {
return function(certificate_white_list_json, generate_certificate_exceptions_url,
certificate_exception_view_url, generate_bulk_certificate_exceptions_url,
active_certificate){
active_certificate) {
var certificateWhiteList = new CertificateWhiteListCollection(certificate_white_list_json, {
parse: true,
canBeEmpty: true,
@@ -38,8 +37,7 @@
new CertificateBulkWhiteList({
bulk_exception_url: generate_bulk_certificate_exceptions_url
}).render();
};
}
);
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

View File

@@ -1,18 +1,17 @@
// Backbone.js Application Model: CertificateWhitelist
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define){
(function(define) {
'use strict';
define([
'underscore',
'underscore.string',
'backbone',
'gettext'
],
function(_, str, Backbone, gettext){
'underscore',
'underscore.string',
'backbone',
'gettext'
],
function(_, str, Backbone, gettext) {
return Backbone.Model.extend({
idAttribute: 'id',
@@ -24,17 +23,16 @@
certificate_generated: '',
notes: ''
},
initialize: function (attributes, options) {
initialize: function(attributes, options) {
this.url = options.url;
},
validate: function(attrs){
validate: function(attrs) {
if (!str.trim(attrs.user_name) && !str.trim(attrs.user_email)) {
return gettext('Student username/email field is required and can not be empty. ' +
'Kindly fill in username/email and then press "Add to Exception List" button.');
}
}
});
}
);
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

View File

@@ -1,14 +1,13 @@
// Backbone.js Application Model: CertificateInvalidation
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define) {
(function(define) {
'use strict';
define(
['underscore', 'underscore.string', 'gettext', 'backbone'],
function(_, str, gettext, Backbone) {
return Backbone.Model.extend({
idAttribute: 'id',
@@ -19,7 +18,7 @@
notes: ''
},
initialize: function (attributes, options) {
initialize: function(attributes, options) {
this.url = options.url;
},
@@ -33,4 +32,4 @@
});
}
);
}).call(this, define || RequireJS.define);
}).call(this, define || RequireJS.define);

View File

@@ -1,22 +1,22 @@
// Backbone Application View: CertificateBulkWhitelist View
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define){
(function(define) {
'use strict';
define([
'jquery',
'underscore',
'gettext',
'backbone'
],
'jquery',
'underscore',
'gettext',
'backbone'
],
function($, _, gettext, Backbone){
function($, _, gettext, Backbone) {
var DOM_SELECTORS = {
bulk_exception: ".bulk-white-list-exception",
upload_csv_button: ".upload-csv-button",
browse_file: ".browse-file",
bulk_white_list_exception_form: "form#bulk-white-list-exception-form"
bulk_exception: '.bulk-white-list-exception',
upload_csv_button: '.upload-csv-button',
browse_file: '.browse-file',
bulk_white_list_exception_form: 'form#bulk-white-list-exception-form'
};
var MESSAGE_GROUP = {
@@ -36,19 +36,19 @@
'click .arrow': 'toggleMessageDetails'
},
initialize: function(options){
initialize: function(options) {
// Re-render the view when an item is added to the collection
this.bulk_exception_url = options.bulk_exception_url;
},
render: function(){
render: function() {
var template = this.loadTemplate('certificate-bulk-white-list');
this.$el.html(template());
},
loadTemplate: function(name) {
var templateSelector = "#" + name + "-tpl",
templateText = $(templateSelector).text();
var templateSelector = '#' + name + '-tpl',
templateText = $(templateSelector).text();
return _.template(templateText);
},
@@ -57,7 +57,7 @@
var self = this;
form.unbind('submit').submit(function(e) {
var data = new FormData(e.currentTarget);
$.ajax({
$.ajax({
dataType: 'json',
type: 'POST',
url: self.bulk_exception_url,
@@ -67,13 +67,13 @@
success: function(data_from_server) {
self.display_response(data_from_server);
}
});
});
e.preventDefault(); // avoid to execute the actual submit of the form.
});
},
display_response: function(data_from_server) {
$(".bulk-exception-results").removeClass('hidden').empty();
$('.bulk-exception-results').removeClass('hidden').empty();
// Display general error messages
if (data_from_server.general_errors.length) {
@@ -138,54 +138,54 @@
$('<div/>', {
class: 'message ' + group
}).appendTo('.bulk-exception-results').prepend(
"<button type='button' id= '" + group + "' class='arrow'> + </button>" + heading
"<button type='button' id= '" + group + "' class='arrow'> + </button>" + heading
).append($('<ul/>', {
class: group
}));
class: group
}));
for(var i = 0; i < display_data.length; i++){
for (var i = 0; i < display_data.length; i++) {
$('<li/>', {
text: display_data[i]
}).appendTo('div.message > .' + group);
}
$("div.message > ." + group).hide();
$('div.message > .' + group).hide();
}
function get_text(qty, group) {
// inner function to display appropriate heading text
var text;
switch(group) {
case MESSAGE_GROUP.successfully_added:
text = qty > 1 ? gettext(qty + ' learners are successfully added to exception list'):
switch (group) {
case MESSAGE_GROUP.successfully_added:
text = qty > 1 ? gettext(qty + ' learners are successfully added to exception list') :
gettext(qty + ' learner is successfully added to the exception list');
break;
break;
case MESSAGE_GROUP.data_format_error:
text = qty > 1 ? gettext(qty + ' records are not in correct format and not added to' +
' the exception list'):
case MESSAGE_GROUP.data_format_error:
text = qty > 1 ? gettext(qty + ' records are not in correct format and not added to' +
' the exception list') :
gettext(qty + ' record is not in correct format and not added to the exception' +
' list');
break;
break;
case MESSAGE_GROUP.user_not_exist:
text = qty > 1 ? gettext(qty + ' learners do not exist in LMS and not added to the' +
' exception list'):
case MESSAGE_GROUP.user_not_exist:
text = qty > 1 ? gettext(qty + ' learners do not exist in LMS and not added to the' +
' exception list') :
gettext(qty + ' learner does not exist in LMS and not added to the exception list');
break;
break;
case MESSAGE_GROUP.user_already_white_listed:
text = qty > 1 ? gettext(qty + ' learners are already white listed and not added to' +
' the exception list'):
case MESSAGE_GROUP.user_already_white_listed:
text = qty > 1 ? gettext(qty + ' learners are already white listed and not added to' +
' the exception list') :
gettext(qty + ' learner is already white listed and not added to the exception ' +
'list');
break;
break;
case MESSAGE_GROUP.user_not_enrolled:
text = qty > 1 ? gettext(qty + ' learners are not enrolled in course and not added to' +
' the exception list'):
case MESSAGE_GROUP.user_not_enrolled:
text = qty > 1 ? gettext(qty + ' learners are not enrolled in course and not added to' +
' the exception list') :
gettext(qty + ' learner is not enrolled in course and not added to the exception' +
' list');
break;
break;
}
return text;
}
@@ -194,11 +194,11 @@
toggleMessageDetails: function(event) {
if (event && event.preventDefault) { event.preventDefault(); }
var group = event.target.id;
$("div.message > ." + group).slideToggle( "fast", function() {
$('div.message > .' + group).slideToggle('fast', function() {
if ($(this).is(':visible')) {
event.target.text = ' -- ';
} else {
event.target.text = ' + ';
event.target.text = ' + ';
}
});
},
@@ -208,7 +208,7 @@
if (event.currentTarget.files.length === 1) {
this.$el.find(DOM_SELECTORS.upload_csv_button).removeAttr('disabled');
this.$el.find(DOM_SELECTORS.browse_file).val(
event.currentTarget.value.substring(event.currentTarget.value.lastIndexOf("\\") + 1));
event.currentTarget.value.substring(event.currentTarget.value.lastIndexOf('\\') + 1));
}
}
});

View File

@@ -1,15 +1,15 @@
// Backbone Application View: CertificateInvalidationView
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define) {
(function(define) {
'use strict';
define(
['jquery', 'underscore', 'gettext', 'backbone', 'js/certificates/models/certificate_invalidation'],
function($, _, gettext, Backbone, CertificateInvalidationModel) {
return Backbone.View.extend({
el: "#certificate-invalidation",
messages: "div.message",
el: '#certificate-invalidation',
messages: 'div.message',
events: {
'click #invalidate-certificate': 'invalidateCertificate',
'click .re-validate-certificate': 'reValidateCertificate'
@@ -25,15 +25,15 @@
},
loadTemplate: function(name) {
var templateSelector = "#" + name + "-tpl",
templateText = $(templateSelector).text();
var templateSelector = '#' + name + '-tpl',
templateText = $(templateSelector).text();
return _.template(templateText);
},
invalidateCertificate: function() {
var user = this.$("#certificate-invalidation-user").val();
var notes = this.$("#certificate-invalidation-notes").val();
var message = "";
var user = this.$('#certificate-invalidation-user').val();
var notes = this.$('#certificate-invalidation-notes').val();
var message = '';
var certificate_invalidation = new CertificateInvalidationModel(
{
@@ -65,14 +65,13 @@
var response_data = JSON.parse(response.responseText);
self.escapeAndShowMessage(response_data.message);
}
catch(exception) {
catch (exception) {
self.escapeAndShowMessage(
gettext("Server Error, Please refresh the page and try again.")
gettext('Server Error, Please refresh the page and try again.')
);
}
}
});
}
else {
this.escapeAndShowMessage(certificate_invalidation.validationError);
@@ -96,9 +95,9 @@
var response_data = JSON.parse(response.responseText);
self.escapeAndShowMessage(response_data.message);
}
catch(exception) {
catch (exception) {
self.escapeAndShowMessage(
gettext("Server Error, Please refresh the page and try again.")
gettext('Server Error, Please refresh the page and try again.')
);
}
},
@@ -119,8 +118,8 @@
},
escapeAndShowMessage: function(message) {
$(this.messages + ">p" ).remove();
this.$(this.messages).removeClass('hidden').append("<p>"+ _.escape(message) + "</p>");
$(this.messages + '>p').remove();
this.$(this.messages).removeClass('hidden').append('<p>' + _.escape(message) + '</p>');
}
});

View File

@@ -1,19 +1,19 @@
// Backbone Application View: CertificateWhitelist View
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define){
(function(define) {
'use strict';
define([
'jquery',
'underscore',
'gettext',
'backbone'
],
'jquery',
'underscore',
'gettext',
'backbone'
],
function($, _, gettext, Backbone){
function($, _, gettext, Backbone) {
return Backbone.View.extend({
el: "#white-listed-students",
el: '#white-listed-students',
message_div: 'div.white-listed-students > div.message',
generate_exception_certificates_radio:
'input:radio[name=generate-exception-certificates-radio]:checked',
@@ -23,35 +23,35 @@
'click .delete-exception': 'removeException'
},
initialize: function(options){
initialize: function(options) {
this.certificateWhiteListEditorView = options.certificateWhiteListEditorView;
this.active_certificate = options.active_certificate;
// Re-render the view when an item is added to the collection
this.listenTo(this.collection, 'change add remove', this.render);
},
render: function(){
render: function() {
var template = this.loadTemplate('certificate-white-list');
this.$el.html(template({certificates: this.collection.models}));
if (!this.active_certificate || this.collection.isEmpty()){
this.$("#generate-exception-certificates").attr("disabled", "disabled");
if (!this.active_certificate || this.collection.isEmpty()) {
this.$('#generate-exception-certificates').attr('disabled', 'disabled');
}
else {
this.$("#generate-exception-certificates").removeAttr("disabled");
this.$('#generate-exception-certificates').removeAttr('disabled');
}
},
loadTemplate: function(name) {
var templateSelector = "#" + name + "-tpl",
templateText = $(templateSelector).text();
var templateSelector = '#' + name + '-tpl',
templateText = $(templateSelector).text();
return _.template(templateText);
},
removeException: function(event){
removeException: function(event) {
var certificate = $(event.target).data();
var model = this.collection.findWhere(certificate);
var self = this;
if(model){
if (model) {
model.destroy(
{
success: function() {
@@ -65,41 +65,41 @@
}
);
}
else{
else {
this.escapeAndShowMessage(
gettext('Could not find Certificate Exception in white list. Please refresh the page and try again') // eslint-disable-line max-len
);
}
},
generateExceptionCertificates: function(){
generateExceptionCertificates: function() {
this.collection.sync(
{success: this.showSuccess(this), error: this.showError(this)},
$(this.generate_exception_certificates_radio).val()
);
},
escapeAndShowMessage: function(message){
$(this.message_div + ">p" ).remove();
$(this.message_div).removeClass('hidden').append("<p>"+ _.escape(message) + "</p>").focus();
$(this.message_div).fadeOut(6000, "linear");
escapeAndShowMessage: function(message) {
$(this.message_div + '>p').remove();
$(this.message_div).removeClass('hidden').append('<p>' + _.escape(message) + '</p>').focus();
$(this.message_div).fadeOut(6000, 'linear');
},
showSuccess: function(caller_object){
return function(xhr){
showSuccess: function(caller_object) {
return function(xhr) {
caller_object.escapeAndShowMessage(xhr.message);
};
},
showError: function(caller_object){
return function(xhr){
try{
showError: function(caller_object) {
return function(xhr) {
try {
var response = JSON.parse(xhr.responseText);
caller_object.escapeAndShowMessage(response.message);
}
catch(exception){
catch (exception) {
caller_object.escapeAndShowMessage(
gettext("Server Error, Please refresh the page and try again.")
gettext('Server Error, Please refresh the page and try again.')
);
}
};

View File

@@ -1,45 +1,45 @@
// Backbone Application View: CertificateWhiteList Editor View
/*global define, RequireJS */
/* global define, RequireJS */
;(function(define){
(function(define) {
'use strict';
define([
'jquery',
'underscore',
'gettext',
'backbone',
'js/certificates/models/certificate_exception'
],
function($, _, gettext, Backbone, CertificateExceptionModel){
'jquery',
'underscore',
'gettext',
'backbone',
'js/certificates/models/certificate_exception'
],
function($, _, gettext, Backbone, CertificateExceptionModel) {
return Backbone.View.extend({
el: "#certificate-white-list-editor",
el: '#certificate-white-list-editor',
message_div: '.message',
events: {
'click #add-exception': 'addException'
},
render: function(){
render: function() {
var template = this.loadTemplate('certificate-white-list-editor');
this.$el.html(template());
},
loadTemplate: function(name) {
var templateSelector = "#" + name + "-tpl",
templateText = $(templateSelector).text();
var templateSelector = '#' + name + '-tpl',
templateText = $(templateSelector).text();
return _.template(templateText);
},
addException: function(){
var value = this.$("#certificate-exception").val();
var notes = this.$("#notes").val();
var user_email = '', user_name='', model={};
addException: function() {
var value = this.$('#certificate-exception').val();
var notes = this.$('#notes').val();
var user_email = '', user_name = '', model = {};
if(this.isEmailAddress(value)){
if (this.isEmailAddress(value)) {
user_email = value;
model = {user_email: user_email};
}
else{
else {
user_name = value;
model = {user_name: user_name};
}
@@ -55,15 +55,15 @@
url: this.collection.url
}
);
var message = "";
var message = '';
if(this.collection.findWhere(model)){
message = gettext("<%= user %> already in exception list.");
if (this.collection.findWhere(model)) {
message = gettext('<%= user %> already in exception list.');
this.escapeAndShowMessage(
_.template(message)({user: (user_name || user_email)})
);
}
else if(certificate_exception.isValid()){
else if (certificate_exception.isValid()) {
message = gettext('<%= user %> has been successfully added to the exception list. Click Generate Exception Certificate below to send the certificate.'); // eslint-disable-line max-len
certificate_exception.save(
null,
@@ -76,9 +76,8 @@
error: this.showError(this)
}
);
}
else{
else {
this.escapeAndShowMessage(certificate_exception.validationError);
}
},
@@ -88,29 +87,29 @@
return re.test(email);
},
escapeAndShowMessage: function(message){
$(this.message_div + ">p" ).remove();
this.$(this.message_div).removeClass('hidden').append("<p>"+ _.escape(message) + "</p>");
escapeAndShowMessage: function(message) {
$(this.message_div + '>p').remove();
this.$(this.message_div).removeClass('hidden').append('<p>' + _.escape(message) + '</p>');
},
showSuccess: function(caller, add_model, message){
return function(model){
if(add_model){
showSuccess: function(caller, add_model, message) {
return function(model) {
if (add_model) {
caller.collection.add(model);
}
caller.escapeAndShowMessage(message);
};
},
showError: function(caller){
return function(model, response){
try{
showError: function(caller) {
return function(model, response) {
try {
var response_data = JSON.parse(response.responseText);
caller.escapeAndShowMessage(response_data.message);
}
catch(exception){
catch (exception) {
caller.escapeAndShowMessage(
gettext("Server Error, Please refresh the page and try again.")
gettext('Server Error, Please refresh the page and try again.')
);
}
};

View File

@@ -3,13 +3,13 @@
*/
var edx = edx || {};
(function ($, _) {
(function($, _) {
'use strict';
edx.commerce = edx.commerce || {};
edx.commerce.credit = edx.commerce.credit || {};
edx.commerce.credit.createCreditRequest = function (providerId, courseKey, username) {
edx.commerce.credit.createCreditRequest = function(providerId, courseKey, username) {
return $.ajax({
url: '/api/credit/v1/providers/' + providerId + '/request/',
type: 'POST',
@@ -23,7 +23,7 @@ var edx = edx || {};
'username': username
}),
context: this,
success: function (requestData) {
success: function(requestData) {
var $form = $('<form>', {
'class': 'hidden',
'action': requestData.url,
@@ -31,7 +31,7 @@ var edx = edx || {};
'accept-method': 'UTF-8'
});
_.each(requestData.parameters, function (value, key) {
_.each(requestData.parameters, function(value, key) {
$('<textarea>').attr({
name: key,
value: value

View File

@@ -5,7 +5,7 @@
/* globals _, Backbone */
var edx = edx || {};
(function ($, _, Backbone) {
(function($, _, Backbone) {
'use strict';
edx.commerce = edx.commerce || {};
@@ -15,7 +15,7 @@ var edx = edx || {};
ecommerceBasketId: null,
ecommerceOrderNumber: null,
initialize: function () {
initialize: function() {
this.ecommerceBasketId = $.url('?basket_id');
this.ecommerceOrderNumber = $.url('?orderNum');
this.useEcommerceApi = this.ecommerceBasketId || this.ecommerceOrderNumber;
@@ -66,50 +66,50 @@ var edx = edx || {};
self.renderError();
});
},
renderCourseNamePlaceholder: function (courseId) {
renderCourseNamePlaceholder: function(courseId) {
// Display the course Id or name (if available) in the placeholder
var $courseNamePlaceholder = $(".course_name_placeholder");
var $courseNamePlaceholder = $('.course_name_placeholder');
$courseNamePlaceholder.text(courseId);
this.getCourseData(courseId).then(function (responseData) {
this.getCourseData(courseId).then(function(responseData) {
$courseNamePlaceholder.text(responseData.name);
});
},
renderUserFullNamePlaceholder: function (username) {
renderUserFullNamePlaceholder: function(username) {
var userModel = Backbone.Model.extend({
urlRoot: '/api/user/v1/accounts/',
urlRoot: '/api/user/v1/accounts/',
url: function() {
return this.urlRoot + this.id;
}
});
this.user = new userModel({id:username});
this.user = new userModel({id: username});
this.user.fetch({success: function(userData) {
$(".full_name_placeholder").text(userData.get('name'));
$('.full_name_placeholder').text(userData.get('name'));
}});
},
renderProvider: function (context) {
var templateHtml = $("#provider-tpl").html(),
providerDiv = this.$el.find("#receipt-provider");
renderProvider: function(context) {
var templateHtml = $('#provider-tpl').html(),
providerDiv = this.$el.find('#receipt-provider');
context.course_key = this.courseKey;
context.username = this.username;
context.platformName = this.$el.data('platform-name');
providerDiv.html(_.template(templateHtml)(context)).removeClass('hidden');
},
renderError: function () {
renderError: function() {
// Display an error
$('#error-container').removeClass('hidden');
},
trackPurchase: function (order) {
window.analytics.track("Completed Order", {
trackPurchase: function(order) {
window.analytics.track('Completed Order', {
orderId: order.number,
total: order.total_excl_tax,
currency: order.currency
});
},
render: function () {
render: function() {
var self = this,
orderId = this.ecommerceOrderNumber || this.ecommerceBasketId || $.url('?payment-order-num');
@@ -122,7 +122,7 @@ var edx = edx || {};
}
},
trackLinks: function () {
trackLinks: function() {
var $verifyNowButton = $('#verify_now_button'),
$verifyLaterButton = $('#verify_later_button');
@@ -145,12 +145,12 @@ var edx = edx || {};
* @param {string} orderId Identifier of the order that was purchased.
* @return {object} JQuery Promise.
*/
getReceiptData: function (orderId) {
getReceiptData: function(orderId) {
var urlFormat = '/shoppingcart/receipt/{orderId}/';
if (this.ecommerceOrderNumber) {
urlFormat = '/api/commerce/v1/orders/{orderId}/';
} else if (this.ecommerceBasketId){
} else if (this.ecommerceBasketId) {
urlFormat = '/api/commerce/v0/baskets/{orderId}/order/';
}
@@ -165,7 +165,7 @@ var edx = edx || {};
* @param {string} providerId The providerId of the credit provider.
* @return {object} JQuery Promise.
*/
getProviderData: function (providerId) {
getProviderData: function(providerId) {
var providerUrl = '/api/credit/v1/providers/{providerId}/';
return $.ajax({
@@ -183,7 +183,7 @@ var edx = edx || {};
* @param {string} courseId The courseId of the course.
* @return {object} JQuery Promise.
*/
getCourseData: function (courseId) {
getCourseData: function(courseId) {
var courseDetailUrl = '/api/courses/v1/courses/{courseId}/';
return $.ajax({
url: edx.StringUtils.interpolate(courseDetailUrl, {courseId: courseId}),
@@ -199,7 +199,7 @@ var edx = edx || {};
* @param {object} order Receipt data received from the server
* @return {object} Receipt template context.
*/
receiptContext: function (order) {
receiptContext: function(order) {
var self = this,
receiptContext;
@@ -222,12 +222,12 @@ var edx = edx || {};
state: order.billing_address.state,
postalCode: order.billing_address.postcode,
country: order.billing_address.country
}
};
}
receiptContext.items = _.map(
order.lines,
function (line) {
function(line) {
return {
lineDescription: line.description,
cost: self.formatMoney(line.line_price_excl_tax)
@@ -240,7 +240,7 @@ var edx = edx || {};
currency: order.currency,
purchasedDatetime: order.purchase_datetime,
totalCost: self.formatMoney(order.total_cost),
isRefunded: order.status === "refunded",
isRefunded: order.status === 'refunded',
billedTo: {
firstName: order.billed_to.first_name,
lastName: order.billed_to.last_name,
@@ -254,7 +254,7 @@ var edx = edx || {};
receiptContext.items = _.map(
order.items,
function (item) {
function(item) {
return {
lineDescription: item.line_desc,
cost: self.formatMoney(item.line_cost)
@@ -266,13 +266,13 @@ var edx = edx || {};
return receiptContext;
},
getOrderCourseKey: function (order) {
getOrderCourseKey: function(order) {
var length, items;
if (this.useEcommerceApi) {
length = order.lines.length;
for (var i = 0; i < length; i++) {
var line = order.lines[i],
attributeValues = _.find(line.product.attribute_values, function (attribute) {
attributeValues = _.find(line.product.attribute_values, function(attribute) {
// If the attribute has a 'code' property, compare its value, otherwise compare 'name'
var value_to_match = 'course_key';
if (attribute.code) {
@@ -288,7 +288,7 @@ var edx = edx || {};
}
}
} else {
items = _.filter(order.items, function (item) {
items = _.filter(order.items, function(item) {
return item.course_key;
});
@@ -300,7 +300,7 @@ var edx = edx || {};
return null;
},
formatMoney: function (moneyStr) {
formatMoney: function(moneyStr) {
return Number(moneyStr).toFixed(2);
},
@@ -310,11 +310,11 @@ var edx = edx || {};
* @param {object} order Receipt data received from the server
* @return {string} String of the provider_id or null.
*/
getCreditProviderId: function (order) {
getCreditProviderId: function(order) {
var attributeValues,
line = order.lines[0];
if (this.useEcommerceApi) {
attributeValues = _.find(line.product.attribute_values, function (attribute) {
attributeValues = _.find(line.product.attribute_values, function(attribute) {
return attribute.name === 'credit_provider';
});
@@ -335,10 +335,10 @@ var edx = edx || {};
function completeOrder(event) {
'use strict';
var courseKey = $(event).data("course-key"),
username = $(event).data("username"),
providerId = $(event).data("provider"),
$errorContainer = $("#error-container");
var courseKey = $(event).data('course-key'),
username = $(event).data('username'),
providerId = $(event).data('provider'),
$errorContainer = $('#error-container');
try {
event.preventDefault();
@@ -347,14 +347,14 @@ function completeOrder(event) {
}
analytics.track(
"edx.bi.credit.clicked_complete_credit",
'edx.bi.credit.clicked_complete_credit',
{
category: "credit",
category: 'credit',
label: courseKey
}
);
edx.commerce.credit.createCreditRequest(providerId, courseKey, username).fail(function () {
$errorContainer.removeClass("hidden");
edx.commerce.credit.createCreditRequest(providerId, courseKey, username).fail(function() {
$errorContainer.removeClass('hidden');
});
}

View File

@@ -15,18 +15,18 @@
* - actionUrl (string or function): URL to navigate to when the action button is clicked. Defaults to the empty string.
* - actionContent: Content of the action button. This may include HTML. Default to the empty string.
*/
;(function (define) {
(function(define) {
'use strict';
define(['jquery',
'underscore',
'backbone',
'text!templates/components/card/card.underscore'],
function ($, _, Backbone, cardTemplate) {
function($, _, Backbone, cardTemplate) {
var CardView = Backbone.View.extend({
tagName: 'li',
events: {
'click .action' : 'action'
'click .action': 'action'
},
/**
@@ -35,25 +35,25 @@
* depends on this.configuration being set to pick the appropriate class. Therefore, configuration
* is set in the constructor, but the rest of the initialization happens in initialize.
*/
constructor: function (options) {
if (!this.configuration) {
constructor: function(options) {
if (!this.configuration) {
this.configuration = (options && options.configuration) ? options.configuration : 'square_card';
}
Backbone.View.prototype.constructor.apply(this, arguments);
},
initialize: function () {
initialize: function() {
this.render();
},
template: _.template(cardTemplate),
switchOnConfiguration: function (square_result, list_result) {
switchOnConfiguration: function(square_result, list_result) {
return this.callIfFunction(this.configuration) === 'square_card' ?
square_result : list_result;
},
callIfFunction: function (value) {
callIfFunction: function(value) {
if ($.isFunction(value)) {
return value.call(this);
} else {
@@ -61,7 +61,7 @@
}
},
className: function () {
className: function() {
var result = 'card ' +
this.switchOnConfiguration('square-card', 'list-card') + ' ' +
this.callIfFunction(this.cardClass);
@@ -71,11 +71,11 @@
return result;
},
render: function () {
render: function() {
var maxLength = 72,
description = this.callIfFunction(this.description);
if (description.length > maxLength) {
description = description.substring(0, maxLength).trim() + '...'
description = description.substring(0, maxLength).trim() + '...';
}
this.$el.html(this.template({
pennant: this.callIfFunction(this.pennant),
@@ -88,7 +88,7 @@
srInfo: this.srInfo
}));
var detailsEl = this.$el.find('.card-meta');
_.each(this.callIfFunction(this.details), function (detail) {
_.each(this.callIfFunction(this.details), function(detail) {
// Call setElement to rebind event handlers
detail.setElement(detail.el).render();
detail.$el.addClass('meta-detail');
@@ -97,7 +97,7 @@
return this;
},
action: function () { },
action: function() { },
cardClass: '',
pennant: '',
title: '',

View File

@@ -1,18 +1,18 @@
/**
* A generic header model.
*/
;(function (define) {
'use strict';
define(['backbone'], function (Backbone) {
var HeaderModel = Backbone.Model.extend({
defaults: {
'title': '',
'description': '',
'breadcrumbs': null,
'nav_aria_label': ''
}
});
(function(define) {
'use strict';
define(['backbone'], function(Backbone) {
var HeaderModel = Backbone.Model.extend({
defaults: {
'title': '',
'description': '',
'breadcrumbs': null,
'nav_aria_label': ''
}
});
return HeaderModel;
});
return HeaderModel;
});
}).call(this, define || RequireJS.define);

View File

@@ -1,19 +1,19 @@
/**
* A generic header view class.
*/
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'text!templates/components/header/header.underscore'],
function (Backbone, headerTemplate) {
function(Backbone, headerTemplate) {
var HeaderView = Backbone.View.extend({
initialize: function (options) {
initialize: function(options) {
this.template = _.template(headerTemplate);
this.headerActionsView = options.headerActionsView;
this.listenTo(this.model, 'change', this.render);
this.render();
},
render: function () {
render: function() {
var json = this.model.attributes;
this.$el.html(this.template(json));
if (this.headerActionsView) {

View File

@@ -1,11 +1,11 @@
$(document).ready(function () {
$('ul.tabs li').click(function() {
$('ul.tabs li').removeClass("enabled");
$(this).addClass("enabled");
$(document).ready(function() {
$('ul.tabs li').click(function() {
$('ul.tabs li').removeClass('enabled');
$(this).addClass('enabled');
var data_class = '.' + $(this).attr('data-class');
var data_class = '.' + $(this).attr('data-class');
$('.tab').slideUp();
$(data_class + ':hidden').slideDown();
})
$('.tab').slideUp();
$(data_class + ':hidden').slideDown();
});
});

View File

@@ -1,16 +1,15 @@
$(function() {
// adding js class for styling with accessibility in mind
$('body').addClass('js');
// form field label styling on focus
$("form :input").focus(function() {
$("label[for='" + this.id + "']").parent().addClass("is-focused");
$('form :input').focus(function() {
$("label[for='" + this.id + "']").parent().addClass('is-focused');
}).blur(function() {
$("label").parent().removeClass("is-focused");
$('label').parent().removeClass('is-focused');
});
$('.status.message.submission-error').addClass("is-hidden");
$('.status.message.submission-error').addClass('is-hidden');
toggleSubmitButton(true);
@@ -25,21 +24,21 @@ $(function() {
$inputs.each(function() {
/* see if it is a required field and - if so - make sure user presented all information */
if (typeof $(this).attr("required") !== typeof undefined) {
if (typeof $(this).attr('required') !== typeof undefined) {
var val = $(this).val();
if (typeof(val) === "string") {
if (typeof(val) === 'string') {
if (val.trim().length === 0) {
var field_label = $(this).parent().find("label");
var field_label = $(this).parent().find('label');
$(this).parent().addClass('field-error');
$('.status.message.submission-error .message-copy').append("<li class='error-item'>"+field_label.text()+"</li>");
$('.status.message.submission-error .message-copy').append("<li class='error-item'>" + field_label.text() + '</li>');
cancel_submit = true;
}
} else if (typeof(val) === "object") {
} else if (typeof(val) === 'object') {
/* for SELECT statements */
if (val === null || val.length === 0 || val[0] === "") {
var field_label = $(this).parent().find("label");
if (val === null || val.length === 0 || val[0] === '') {
var field_label = $(this).parent().find('label');
$(this).parent().addClass('field-error');
$('.status.message.submission-error .message-copy').append("<li class='error-item'>"+field_label.text()+"</li>");
$('.status.message.submission-error .message-copy').append("<li class='error-item'>" + field_label.text() + '</li>');
cancel_submit = true;
}
}
@@ -47,11 +46,11 @@ $(function() {
});
if (cancel_submit) {
$('.status.message.submission-error').
removeClass("is-hidden").
$('.status.message.submission-error').
removeClass('is-hidden').
focus();
$("html, body").animate({ scrollTop: 0 }, "fast");
return false;
$('html, body').animate({scrollTop: 0}, 'fast');
return false;
}
toggleSubmitButton(false);
@@ -71,16 +70,16 @@ $(function() {
json = $.parseJSON(jqXHR.responseText);
$('.status.message.submission-error').addClass('is-shown').focus();
$('.status.message.submission-error .message-copy').
html(gettext("There has been an error processing your survey.")).
html(gettext('There has been an error processing your survey.')).
stop().
css("display", "block");
css('display', 'block');
});
});
function toggleSubmitButton(enable) {
var $submitButton = $('form .form-actions #submit');
if(enable) {
if (enable) {
$submitButton.
removeClass('is-disabled').
attr('aria-disabled', false).

View File

@@ -1,16 +1,16 @@
;(function(define) {
(function(define) {
'use strict';
define(['jquery', 'logger'], function ($, Logger) {
return function () {
$(".accordion-nav").click(function(event) {
define(['jquery', 'logger'], function($, Logger) {
return function() {
$('.accordion-nav').click(function(event) {
Logger.log(
"edx.ui.lms.outline.selected",
'edx.ui.lms.outline.selected',
{
current_url: window.location.href,
target_url: event.currentTarget.href,
target_name: $(this).find("p.accordion-display-name").text(),
widget_placement: "accordion"
target_name: $(this).find('p.accordion-display-name').text(),
widget_placement: 'accordion'
});
});
};

View File

@@ -1,20 +1,20 @@
$(document).ready(function() {
'use strict';
$(".generate_certs").click(function(e){
$('.generate_certs').click(function(e) {
e.preventDefault();
var post_url = $(".generate_certs").data("endpoint");
$(".generate_certs").attr("disabled", true).addClass('is-disabled').attr('aria-disabled', true);
var post_url = $('.generate_certs').data('endpoint');
$('.generate_certs').attr('disabled', true).addClass('is-disabled').attr('aria-disabled', true);
$.ajax({
type: "POST",
type: 'POST',
url: post_url,
dataType: 'text',
success: function () {
success: function() {
location.reload();
},
error: function(jqXHR, textStatus, errorThrown) {
$('#errors-info').html(jqXHR.responseText);
$(".generate_certs").attr("disabled", false).removeClass('is-disabled').attr('aria-disabled', false);
$('.generate_certs').attr('disabled', false).removeClass('is-disabled').attr('aria-disabled', false);
}
});
});

View File

@@ -1,14 +1,14 @@
;(function(define) {
(function(define) {
'use strict';
define(['jquery', 'logger'], function ($, Logger) {
return function () {
$('.last-accessed-link').on('click', function (event) {
define(['jquery', 'logger'], function($, Logger) {
return function() {
$('.last-accessed-link').on('click', function(event) {
Logger.log('edx.course.home.resume_course.clicked', {
url: event.currentTarget.href
});
});
$('.date-summary-verified-upgrade-deadline .date-summary-link').on('click', function () {
$('.date-summary-verified-upgrade-deadline .date-summary-link').on('click', function() {
Logger.log('edx.course.home.upgrade_verified.clicked', {});
});
};

View File

@@ -1,18 +1,18 @@
;(function (define) {
(function(define) {
'use strict';
define([
'jquery',
'logger',
'js/bookmarks/views/bookmarks_list_button'
],
'jquery',
'logger',
'js/bookmarks/views/bookmarks_list_button'
],
function($, Logger, BookmarksListButton) {
return function() {
// This function performs all actions common to all courseware.
// 1. adding an event to all link clicks.
$('a:not([href^="#"])').click(function(event) {
Logger.log(
"edx.ui.lms.link_clicked",
'edx.ui.lms.link_clicked',
{
current_url: window.location.href,
target_url: event.currentTarget.href

View File

@@ -10,8 +10,8 @@ $(document).ready(function() {
var el = $(this);
container.toggleClass('is-hidden');
el.find('.fa').toggleClass('fa-caret-up fa-caret-down');
el.find('.requirement-detail').text(function(i, text){
return text === gettext('Less') ? gettext('More') : gettext('Less');
el.find('.requirement-detail').text(function(i, text) {
return text === gettext('Less') ? gettext('More') : gettext('Less');
});
});
});

View File

@@ -1,14 +1,13 @@
;(function (define) {
(function(define) {
'use strict';
define(['jquery', 'logger', 'moment'],
function ($, Logger, moment) {
return function () {
function($, Logger, moment) {
return function() {
// define variables for code legibility
var toggleActionElements = $('.toggle-visibility-button');
var updateToggleActionText = function (elementIsHidden, actionElement) {
var updateToggleActionText = function(elementIsHidden, actionElement) {
var show_text = actionElement.data('show'),
hide_text = actionElement.data('hide'),
first_hidden_update = $('.old-updates .toggle-visibility-button').first();
@@ -29,7 +28,7 @@
}
};
$.each(toggleActionElements, function (i, elem) {
$.each(toggleActionElements, function(i, elem) {
var toggleActionElement = $(elem),
toggleTargetElement = toggleActionElement.siblings('.toggle-visibility-element'),
elementIsHidden = toggleTargetElement.is(':visible'),
@@ -37,7 +36,7 @@
updateToggleActionText(elementIsHidden, toggleActionElement);
toggleActionElement.on('click', function (event) {
toggleActionElement.on('click', function(event) {
event.preventDefault();
toggleTargetElement.toggleClass('hidden');
updateToggleActionText(!toggleTargetElement.hasClass('hidden'), toggleActionElement);

View File

@@ -4,24 +4,24 @@
var edx = edx || {};
(function ($, analytics) {
(function($, analytics) {
'use strict';
$(document).ready(function () {
var $errorContainer = $(".credit-error-msg"),
creditStatusError = $errorContainer.data("credit-error");
$(document).ready(function() {
var $errorContainer = $('.credit-error-msg'),
creditStatusError = $errorContainer.data('credit-error');
if (creditStatusError === "True") {
$errorContainer.toggleClass("is-hidden");
if (creditStatusError === 'True') {
$errorContainer.toggleClass('is-hidden');
}
// Fire analytics events when the "purchase credit" button is clicked
$(".purchase-credit-btn").on("click", function (event) {
var courseKey = $(event.target).data("course-key");
$('.purchase-credit-btn').on('click', function(event) {
var courseKey = $(event.target).data('course-key');
analytics.track(
"edx.bi.credit.clicked_purchase_credit",
'edx.bi.credit.clicked_purchase_credit',
{
category: "credit",
category: 'credit',
label: courseKey
}
);
@@ -30,17 +30,17 @@ var edx = edx || {};
// This event invokes credit request endpoint. It will initiate
// a credit request for the credit course for the provided user.
$(".pending-credit-btn").on("click", function (event) {
$('.pending-credit-btn').on('click', function(event) {
var $target = $(event.target),
courseKey = $target.data("course-key"),
username = $target.data("user"),
providerId = $target.data("provider");
courseKey = $target.data('course-key'),
username = $target.data('user'),
providerId = $target.data('provider');
event.preventDefault();
edx.commerce.credit.createCreditRequest(providerId, courseKey, username).fail(function () {
$(".credit-action").hide();
$errorContainer.toggleClass("is-hidden");
edx.commerce.credit.createCreditRequest(providerId, courseKey, username).fail(function() {
$('.credit-action').hide();
$errorContainer.toggleClass('is-hidden');
});
});
});

View File

@@ -23,12 +23,12 @@ var edx = edx || {};
* @param {Object} params - Form data, included as hidden inputs.
*/
var configureForm = function(form, method, url, params) {
$("input", form).remove();
form.attr("action", url);
form.attr("method", method);
$('input', form).remove();
form.attr('action', url);
form.attr('method', method);
_.each(params, function(value, key) {
$("<input>").attr({
type: "hidden",
$('<input>').attr({
type: 'hidden',
name: key,
value: value
}).appendTo(form);
@@ -43,9 +43,9 @@ var edx = edx || {};
*/
var firePaymentAnalyticsEvent = function(course) {
analytics.track(
"edx.bi.user.payment_processor.visited",
'edx.bi.user.payment_processor.visited',
{
category: "donations",
category: 'donations',
label: course
}
);
@@ -62,8 +62,8 @@ var edx = edx || {};
*/
var addDonationToCart = function(amount, course) {
return $.ajax({
url: "/shoppingcart/donation/",
type: "POST",
url: '/shoppingcart/donation/',
type: 'POST',
data: {
amount: amount,
course_id: course
@@ -97,12 +97,12 @@ var edx = edx || {};
* @returns {DonationView}
*/
render: function() {
var html = _.template($("#donation-tpl").html())({});
var html = _.template($('#donation-tpl').html())({});
this.$el.html(html);
this.$amount = $("input[name=\"amount\"]", this.$el);
this.$submit = $(".action-donate", this.$el);
this.$errorMsg = $(".donation-error-msg", this.$el);
this.$paymentForm = $(".payment-form", this.$el);
this.$amount = $('input[name="amount"]', this.$el);
this.$submit = $('.action-donate', this.$el);
this.$errorMsg = $('.donation-error-msg', this.$el);
this.$paymentForm = $('.payment-form', this.$el);
this.$submit.click(this.donate);
return this;
},
@@ -122,7 +122,7 @@ var edx = edx || {};
}
// Immediately disable the submit button to prevent duplicate submissions
this.$submit.addClass("disabled");
this.$submit.addClass('disabled');
if (this.validate()) {
var amount = this.$amount.val();
@@ -132,7 +132,7 @@ var edx = edx || {};
}
else {
// If an error occurred, allow the user to resubmit
this.$submit.removeClass("disabled");
this.$submit.removeClass('disabled');
}
},
@@ -166,13 +166,13 @@ var edx = edx || {};
if (isValid) {
this.$amount.removeClass('validation-error');
this.$errorMsg.text("");
this.$errorMsg.text('');
}
else {
this.$amount.addClass('validation-error');
this.$errorMsg.text(
gettext("Please enter a valid donation amount.")
gettext('Please enter a valid donation amount.')
);
}
@@ -204,10 +204,10 @@ var edx = edx || {};
*/
displayServerError: function() {
// Display the error message
this.$errorMsg.text(gettext("Your donation could not be submitted."));
this.$errorMsg.text(gettext('Your donation could not be submitted.'));
// Re-enable the submit button to allow the user to retry
this.$submit.removeClass("disabled");
this.$submit.removeClass('disabled');
},
/**
@@ -218,7 +218,7 @@ var edx = edx || {};
*/
submitPaymentForm: function(form) {
form.submit();
},
}
};
view.initialize(params);
@@ -231,14 +231,13 @@ var edx = edx || {};
// For each one, create a new donation view to handle
// that form, and parameterize it based on the
// "data-course" attribute (the course ID).
$(".donate-container").each(function() {
$('.donate-container').each(function() {
var container = $(this);
var course = container.data("course");
var course = container.data('course');
var view = new edx.dashboard.donation.DonationView({
el: container,
course: course
}).render();
});
});
})(jQuery);

View File

@@ -1,13 +1,13 @@
var edx = edx || {};
(function ($) {
(function($) {
'use strict';
edx.dashboard = edx.dashboard || {};
edx.dashboard.dropdown = {};
// Generate the properties object to be passed along with business intelligence events.
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu = function (event) {
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu = function(event) {
// define variables for code legibility
var dashboardIndex = $(event.currentTarget).data().dashboardIndex,
dropdown = $('#actions-dropdown-' + dashboardIndex),
@@ -26,33 +26,33 @@ var edx = edx || {};
var itemToFocusIndex;
// if space or escape key pressed
if ( event.which === 32 || event.which === 27) {
dropdownButton.click();
event.preventDefault();
if (event.which === 32 || event.which === 27) {
dropdownButton.click();
event.preventDefault();
}
// if up arrow key pressed or shift+tab
else if (event.which === 38 || (event.which === 9 && event.shiftKey)) {
// if first item go to last
if (focusedItemIndex === 0 || focusedItemIndex === -1) {
menuItems.last().focus();
} else {
itemToFocusIndex = focusedItemIndex - 1;
menuItems.get(itemToFocusIndex).focus();
}
event.preventDefault();
if (focusedItemIndex === 0 || focusedItemIndex === -1) {
menuItems.last().focus();
} else {
itemToFocusIndex = focusedItemIndex - 1;
menuItems.get(itemToFocusIndex).focus();
}
event.preventDefault();
}
// if down arrow key pressed or tab key
else if (event.which === 40 || event.which === 9) {
// if last item go to first
if (focusedItemIndex === menuItems.length - 1 || focusedItemIndex === -1) {
menuItems.first().focus();
} else {
itemToFocusIndex = focusedItemIndex + 1;
menuItems.get(itemToFocusIndex).focus();
}
event.preventDefault();
if (focusedItemIndex === menuItems.length - 1 || focusedItemIndex === -1) {
menuItems.first().focus();
} else {
itemToFocusIndex = focusedItemIndex + 1;
menuItems.get(itemToFocusIndex).focus();
}
event.preventDefault();
}
};
@@ -69,22 +69,21 @@ var edx = edx || {};
// Inform the ARIA framework that the dropdown has been expanded
dropdownButton.attr('aria-expanded', !ariaExpandedState);
//catch keypresses when inside dropdownMenu (we want to catch spacebar;
// catch keypresses when inside dropdownMenu (we want to catch spacebar;
// escape; up arrow or shift+tab; and down arrow or tab)
dropdown.on('keydown', function(event){
catchKeyPress($(this), event);
dropdown.on('keydown', function(event) {
catchKeyPress($(this), event);
});
};
edx.dashboard.dropdown.bindToggleButtons = function() {
$('.action-more').bind(
$('.action-more').bind(
'click',
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu
);
};
$(document).ready(function() {
edx.dashboard.dropdown.bindToggleButtons();
edx.dashboard.dropdown.bindToggleButtons();
});
})(jQuery);

View File

@@ -10,11 +10,11 @@
var edx = edx || {};
(function($, gettext, Logger, accessibleModal, interpolate) {
'use strict';
(function($, gettext, Logger, accessibleModal, interpolate) {
'use strict';
edx.dashboard = edx.dashboard || {};
edx.dashboard.legacy = {};
edx.dashboard = edx.dashboard || {};
edx.dashboard.legacy = {};
/**
* Initialize the dashboard using legacy JavaScript.
@@ -29,32 +29,31 @@
* - changeEmailSettings
* - verifyToggleBannerFailedOff
*/
edx.dashboard.legacy.init = function(urls) {
var notifications = $('.dashboard-notifications'),
upgradeButtonLinks = $('.action-upgrade'),
verifyButtonLinks = $('.verification-cta > .cta');
edx.dashboard.legacy.init = function(urls) {
var notifications = $('.dashboard-notifications'),
upgradeButtonLinks = $('.action-upgrade'),
verifyButtonLinks = $('.verification-cta > .cta');
// On initialization, set focus to the first notification available for screen readers.
if ( notifications.children().length > 0 ) {
notifications.focus();
}
if (notifications.children().length > 0) {
notifications.focus();
}
// Track clicks of the upgrade button. The `trackLink` method is a helper that makes
// a `track` call whenever a bound link is clicked. Usually the page would change before
// `track` had time to execute; `trackLink` inserts a small timeout to give the `track`
// call enough time to fire. The clicked link element is passed to `generateProperties`.
window.analytics.trackLink(upgradeButtonLinks, 'edx.bi.dashboard.upgrade_button.clicked', generateProperties);
window.analytics.trackLink(upgradeButtonLinks, 'edx.bi.dashboard.upgrade_button.clicked', generateProperties);
// Track clicks of the "verify now" button.
window.analytics.trackLink(verifyButtonLinks, 'edx.bi.user.verification.resumed', generateProperties);
window.analytics.trackLink(verifyButtonLinks, 'edx.bi.user.verification.resumed', generateProperties);
// Track clicks of the LinkedIn "Add to Profile" button
window.analytics.trackLink(
window.analytics.trackLink(
$('.action-linkedin-profile'),
'edx.bi.user.linkedin_add_to_profile',
function( element ) {
var $el = $( element );
function(element) {
var $el = $(element);
return {
category: 'linkedin',
label: $el.data('course-id'),
@@ -65,122 +64,122 @@
// Generate the properties object to be passed along with business intelligence events.
function generateProperties(element) {
var $el = $(element),
properties = {};
function generateProperties(element) {
var $el = $(element),
properties = {};
if ( $el.hasClass('action-upgrade') ) {
properties.category = 'upgrade';
} else if ( $el.hasClass('cta') ) {
properties.category = 'verification';
}
if ($el.hasClass('action-upgrade')) {
properties.category = 'upgrade';
} else if ($el.hasClass('cta')) {
properties.category = 'verification';
}
properties.label = $el.data('course-id');
properties.label = $el.data('course-id');
return properties;
}
return properties;
}
$("#failed-verification-button-dismiss").click(function() {
$.ajax({
url: urls.verifyToggleBannerFailedOff,
type: "post"
});
$("#failed-verification-banner").addClass('is-hidden');
});
$('#failed-verification-button-dismiss').click(function() {
$.ajax({
url: urls.verifyToggleBannerFailedOff,
type: 'post'
});
$('#failed-verification-banner').addClass('is-hidden');
});
$("#upgrade-to-verified").click(function(event) {
var user = $(event.target).closest(".action-upgrade").data("user"),
course = $(event.target).closest(".action-upgrade").data("course-id");
$('#upgrade-to-verified').click(function(event) {
var user = $(event.target).closest('.action-upgrade').data('user'),
course = $(event.target).closest('.action-upgrade').data('course-id');
Logger.log('edx.course.enrollment.upgrade.clicked', [user, course], null);
});
Logger.log('edx.course.enrollment.upgrade.clicked', [user, course], null);
});
$(".action-email-settings").click(function(event) {
var element = $(event.target);
$("#email_settings_course_id").val( element.data("course-id") );
$("#email_settings_course_number").text( element.data("course-number") );
if($(event.target).data("optout") === "False") {
$("#receive_emails").prop('checked', true);
}
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event);
});
$('.action-email-settings').click(function(event) {
var element = $(event.target);
$('#email_settings_course_id').val(element.data('course-id'));
$('#email_settings_course_number').text(element.data('course-number'));
if ($(event.target).data('optout') === 'False') {
$('#receive_emails').prop('checked', true);
}
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event);
});
$(".action-unenroll").click(function(event) {
var element = $(event.target);
var track_info = element.data("track-info");
var course_number = element.data("course-number");
var course_name = element.data("course-name");
var cert_name_long = element.data("cert-name-long");
$('#track-info').html(interpolate(track_info, {
course_number: "<span id='unenroll_course_number'>" + course_number + "</span>",
course_name: "<span id='unenroll_course_name'>" + course_name + "</span>",
cert_name_long: "<span id='unenroll_cert_name'>" + cert_name_long + "</span>"
}, true));
$('#refund-info').html( element.data("refund-info") );
$("#unenroll_course_id").val( element.data("course-id") );
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event);
});
$('.action-unenroll').click(function(event) {
var element = $(event.target);
var track_info = element.data('track-info');
var course_number = element.data('course-number');
var course_name = element.data('course-name');
var cert_name_long = element.data('cert-name-long');
$('#track-info').html(interpolate(track_info, {
course_number: "<span id='unenroll_course_number'>" + course_number + '</span>',
course_name: "<span id='unenroll_course_name'>" + course_name + '</span>',
cert_name_long: "<span id='unenroll_cert_name'>" + cert_name_long + '</span>'
}, true));
$('#refund-info').html(element.data('refund-info'));
$('#unenroll_course_id').val(element.data('course-id'));
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event);
});
$('#unenroll_form').on('ajax:complete', function(event, xhr) {
if(xhr.status === 200) {
location.href = urls.dashboard;
} else if (xhr.status === 403) {
location.href = urls.signInUser + "?course_id=" +
encodeURIComponent($("#unenroll_course_id").val()) + "&enrollment_action=unenroll";
} else {
$('#unenroll_error').html(
xhr.responseText ? xhr.responseText : gettext("An error occurred. Please try again later.")
).stop().css("display", "block");
}
});
$('#unenroll_form').on('ajax:complete', function(event, xhr) {
if (xhr.status === 200) {
location.href = urls.dashboard;
} else if (xhr.status === 403) {
location.href = urls.signInUser + '?course_id=' +
encodeURIComponent($('#unenroll_course_id').val()) + '&enrollment_action=unenroll';
} else {
$('#unenroll_error').html(
xhr.responseText ? xhr.responseText : gettext('An error occurred. Please try again later.')
).stop().css('display', 'block');
}
});
$("#email_settings_form").submit(function(){
$.ajax({
type: "POST",
url: urls.changeEmailSettings,
data: $(this).serializeArray(),
success: function(data) {
if(data.success) {
location.href = urls.dashboard;
}
},
error: function(xhr) {
if (xhr.status === 403) {
location.href = urls.signInUser;
}
}
});
return false;
});
$('#email_settings_form').submit(function() {
$.ajax({
type: 'POST',
url: urls.changeEmailSettings,
data: $(this).serializeArray(),
success: function(data) {
if (data.success) {
location.href = urls.dashboard;
}
},
error: function(xhr) {
if (xhr.status === 403) {
location.href = urls.signInUser;
}
}
});
return false;
});
$(".action-email-settings").each(function(index){
$(this).attr("id", "email-settings-" + index);
$('.action-email-settings').each(function(index) {
$(this).attr('id', 'email-settings-' + index);
// a bit of a hack, but gets the unique selector for the modal trigger
var trigger = "#" + $(this).attr("id");
accessibleModal(
var trigger = '#' + $(this).attr('id');
accessibleModal(
trigger,
"#email-settings-modal .close-modal",
"#email-settings-modal",
"#dashboard-main"
'#email-settings-modal .close-modal',
'#email-settings-modal',
'#dashboard-main'
);
});
});
$(".action-unenroll").each(function(index){
$(this).attr("id", "unenroll-" + index);
$('.action-unenroll').each(function(index) {
$(this).attr('id', 'unenroll-' + index);
// a bit of a hack, but gets the unique selector for the modal trigger
var trigger = "#" + $(this).attr("id");
accessibleModal(
var trigger = '#' + $(this).attr('id');
accessibleModal(
trigger,
"#unenroll-modal .close-modal",
"#unenroll-modal",
"#dashboard-main"
'#unenroll-modal .close-modal',
'#unenroll-modal',
'#dashboard-main'
);
});
});
$("#unregister_block_course").click( function(event) {
$("#unenroll_course_id").val($(event.target).data("course-id"));
$("#unenroll_course_number").text($(event.target).data("course-number"));
$("#unenroll_course_name").text($(event.target).data("course-name"));
});
};
})(jQuery, gettext, Logger, accessible_modal, interpolate);
$('#unregister_block_course').click(function(event) {
$('#unenroll_course_id').val($(event.target).data('course-id'));
$('#unenroll_course_number').text($(event.target).data('course-number'));
$('#unenroll_course_name').text($(event.target).data('course-name'));
});
};
})(jQuery, gettext, Logger, accessible_modal, interpolate);

View File

@@ -4,13 +4,13 @@
var edx = edx || {};
(function ($) {
(function($) {
'use strict';
edx.dashboard = edx.dashboard || {};
// Generate the properties object to be passed along with business intelligence events.
edx.dashboard.generateTrackProperties = function(element){
edx.dashboard.generateTrackProperties = function(element) {
var $el = $(element),
properties = {};
@@ -32,7 +32,7 @@ var edx = edx || {};
};
// Emit an event when the 'course title link' is clicked.
edx.dashboard.trackCourseTitleClicked = function($courseTitleLink, properties){
edx.dashboard.trackCourseTitleClicked = function($courseTitleLink, properties) {
var trackProperty = properties || edx.dashboard.generateTrackProperties;
window.analytics.trackLink(
@@ -43,7 +43,7 @@ var edx = edx || {};
};
// Emit an event when the 'course image' is clicked.
edx.dashboard.trackCourseImageLinkClicked = function($courseImageLink, properties){
edx.dashboard.trackCourseImageLinkClicked = function($courseImageLink, properties) {
var trackProperty = properties || edx.dashboard.generateTrackProperties;
window.analytics.trackLink(
$courseImageLink,
@@ -53,7 +53,7 @@ var edx = edx || {};
};
// Emit an event when the 'View Course' button is clicked.
edx.dashboard.trackEnterCourseLinkClicked = function($enterCourseLink, properties){
edx.dashboard.trackEnterCourseLinkClicked = function($enterCourseLink, properties) {
var trackProperty = properties || edx.dashboard.generateTrackProperties;
window.analytics.trackLink(
$enterCourseLink,
@@ -63,7 +63,7 @@ var edx = edx || {};
};
// Emit an event when the options dropdown is engaged.
edx.dashboard.trackCourseOptionDropdownClicked = function($optionsDropdown, properties){
edx.dashboard.trackCourseOptionDropdownClicked = function($optionsDropdown, properties) {
var trackProperty = properties || edx.dashboard.generateTrackProperties;
window.analytics.trackLink(
$optionsDropdown,
@@ -73,7 +73,7 @@ var edx = edx || {};
};
// Emit an event when the 'Learn about verified' link is clicked.
edx.dashboard.trackLearnVerifiedLinkClicked = function($courseLearnVerified, properties){
edx.dashboard.trackLearnVerifiedLinkClicked = function($courseLearnVerified, properties) {
var trackProperty = properties || edx.dashboard.generateTrackProperties;
window.analytics.trackLink(
$courseLearnVerified,
@@ -83,8 +83,8 @@ var edx = edx || {};
};
// Emit an event when the 'Find Courses' button is clicked.
edx.dashboard.trackFindCourseBtnClicked = function($findCoursesBtn, properties){
var trackProperty = properties || { category: 'dashboard', label: null };
edx.dashboard.trackFindCourseBtnClicked = function($findCoursesBtn, properties) {
var trackProperty = properties || {category: 'dashboard', label: null};
window.analytics.trackLink(
$findCoursesBtn,
'edx.bi.dashboard.find_courses_button.clicked',
@@ -93,7 +93,7 @@ var edx = edx || {};
};
// Emit an event when the 'View XSeries Details' button is clicked
edx.dashboard.trackXseriesBtnClicked = function($xseriesBtn, properties){
edx.dashboard.trackXseriesBtnClicked = function($xseriesBtn, properties) {
var trackProperty = properties || edx.dashboard.generateProgramProperties;
window.analytics.trackLink(
$xseriesBtn,
@@ -103,11 +103,10 @@ var edx = edx || {};
};
edx.dashboard.xseriesTrackMessages = function() {
$('.xseries-action .btn').each(function(i, element) {
var data = edx.dashboard.generateProgramProperties($(element));
window.analytics.track( 'edx.bi.dashboard.xseries_cta_message.viewed', data );
window.analytics.track('edx.bi.dashboard.xseries_cta_message.viewed', data);
});
};

View File

@@ -1,108 +1,104 @@
;(function (define) {
(function(define) {
define([
'backbone',
'js/discovery/models/course_card'
], function(Backbone, CourseCard) {
'use strict';
define([
'backbone',
'js/discovery/models/course_card'
], function (Backbone, CourseCard) {
'use strict';
return Backbone.Collection.extend({
return Backbone.Collection.extend({
model: CourseCard,
pageSize: 20,
totalCount: 0,
latestModelsCount: 0,
searchTerm: '',
selectedFacets: {},
facets: {},
page: 0,
url: '/search/course_discovery/',
fetchXhr: null,
model: CourseCard,
pageSize: 20,
totalCount: 0,
latestModelsCount: 0,
searchTerm: '',
selectedFacets: {},
facets: {},
page: 0,
url: '/search/course_discovery/',
fetchXhr: null,
performSearch: function (searchTerm, facets) {
this.fetchXhr && this.fetchXhr.abort();
this.searchTerm = searchTerm || '';
this.selectedFacets = facets || {};
var data = this.preparePostData(0);
this.resetState();
this.fetchXhr = this.fetch({
data: data,
type: 'POST',
success: function (self, xhr) {
self.trigger('search');
},
error: function (self, xhr) {
self.trigger('error');
}
});
},
loadNextPage: function () {
this.fetchXhr && this.fetchXhr.abort();
var data = this.preparePostData(this.page + 1);
this.fetchXhr = this.fetch({
data: data,
type: 'POST',
success: function (self, xhr) {
self.page += 1;
self.trigger('next');
},
error: function (self, xhr) {
self.trigger('error');
},
add: true,
reset: false,
remove: false
});
},
preparePostData: function(pageNumber) {
var data = {
search_string: this.searchTerm,
page_size: this.pageSize,
page_index: pageNumber
};
if(this.selectedFacets.length > 0) {
this.selectedFacets.each(function(facet) {
data[facet.get('type')] = facet.get('query');
performSearch: function(searchTerm, facets) {
this.fetchXhr && this.fetchXhr.abort();
this.searchTerm = searchTerm || '';
this.selectedFacets = facets || {};
var data = this.preparePostData(0);
this.resetState();
this.fetchXhr = this.fetch({
data: data,
type: 'POST',
success: function(self, xhr) {
self.trigger('search');
},
error: function(self, xhr) {
self.trigger('error');
}
});
},
loadNextPage: function() {
this.fetchXhr && this.fetchXhr.abort();
var data = this.preparePostData(this.page + 1);
this.fetchXhr = this.fetch({
data: data,
type: 'POST',
success: function(self, xhr) {
self.page += 1;
self.trigger('next');
},
error: function(self, xhr) {
self.trigger('error');
},
add: true,
reset: false,
remove: false
});
},
preparePostData: function(pageNumber) {
var data = {
search_string: this.searchTerm,
page_size: this.pageSize,
page_index: pageNumber
};
if (this.selectedFacets.length > 0) {
this.selectedFacets.each(function(facet) {
data[facet.get('type')] = facet.get('query');
});
}
return data;
},
parse: function(response) {
var results = response['results'] || [];
this.latestModelsCount = results.length;
this.totalCount = response.total;
if (typeof response.facets !== 'undefined') {
this.facets = response.facets;
}
else {
this.facets = [];
}
return _.map(results, function(result) {
return result.data;
});
},
resetState: function() {
this.reset();
this.page = 0;
this.totalCount = 0;
this.latestModelsCount = 0;
},
hasNextPage: function() {
return this.totalCount - ((this.page + 1) * this.pageSize) > 0;
},
latestModels: function() {
return this.last(this.latestModelsCount);
}
return data;
},
parse: function(response) {
var results = response['results'] || [];
this.latestModelsCount = results.length;
this.totalCount = response.total;
if (typeof response.facets !== 'undefined') {
this.facets = response.facets;
}
else {
this.facets = [];
}
return _.map(results, function (result) {
return result.data;
});
},
resetState: function () {
this.reset();
this.page = 0;
this.totalCount = 0;
this.latestModelsCount = 0;
},
hasNextPage: function () {
return this.totalCount - ((this.page + 1) * this.pageSize) > 0;
},
latestModels: function () {
return this.last(this.latestModelsCount);
}
});
});
});
})(define || RequireJS.define);

View File

@@ -1,18 +1,15 @@
;(function (define) {
(function(define) {
define(['backbone', 'js/discovery/models/filter'], function(Backbone, Filter) {
'use strict';
define(['backbone', 'js/discovery/models/filter'], function (Backbone, Filter) {
'use strict';
return Backbone.Collection.extend({
model: Filter,
getTerms: function () {
return this.reduce(function (terms, filter) {
terms[filter.id] = filter.get('query');
return terms;
}, {});
}
return Backbone.Collection.extend({
model: Filter,
getTerms: function() {
return this.reduce(function(terms, filter) {
terms[filter.id] = filter.get('query');
return terms;
}, {});
}
});
});
});
})(define || RequireJS.define);

View File

@@ -1,31 +1,29 @@
;(function (define) {
(function(define) {
'use strict';
define(['backbone', 'js/discovery/models/search_state', 'js/discovery/collections/filters',
'js/discovery/views/search_form', 'js/discovery/views/courses_listing',
'js/discovery/views/filter_bar', 'js/discovery/views/refine_sidebar'],
function(Backbone, SearchState, Filters, SearchForm, CoursesListing, FilterBar, RefineSidebar) {
return function (meanings, searchQuery) {
return function(meanings, searchQuery) {
var dispatcher = _.extend({}, Backbone.Events);
var search = new SearchState();
var filters = new Filters();
var listing = new CoursesListing({ model: search.discovery });
var listing = new CoursesListing({model: search.discovery});
var form = new SearchForm();
var filterBar = new FilterBar({ collection: filters });
var filterBar = new FilterBar({collection: filters});
var refineSidebar = new RefineSidebar({
collection: search.discovery.facetOptions,
meanings: meanings
});
dispatcher.listenTo(form, 'search', function (query) {
dispatcher.listenTo(form, 'search', function(query) {
filters.reset();
form.showLoadingIndicator();
search.performSearch(query, filters.getTerms());
});
dispatcher.listenTo(refineSidebar, 'selectOption', function (type, query, name) {
dispatcher.listenTo(refineSidebar, 'selectOption', function(type, query, name) {
form.showLoadingIndicator();
if (filters.get(type)) {
removeFilter(type);
@@ -38,19 +36,19 @@
dispatcher.listenTo(filterBar, 'clearFilter', removeFilter);
dispatcher.listenTo(filterBar, 'clearAll', function () {
dispatcher.listenTo(filterBar, 'clearAll', function() {
form.doSearch('');
});
dispatcher.listenTo(listing, 'next', function () {
search.loadNextPage()
dispatcher.listenTo(listing, 'next', function() {
search.loadNextPage();
});
dispatcher.listenTo(search, 'next', function () {
dispatcher.listenTo(search, 'next', function() {
listing.renderNext();
});
dispatcher.listenTo(search, 'search', function (query, total) {
dispatcher.listenTo(search, 'search', function(query, total) {
if (total > 0) {
form.showFoundMessage(total);
if (query) {
@@ -69,7 +67,7 @@
refineSidebar.render();
});
dispatcher.listenTo(search, 'error', function () {
dispatcher.listenTo(search, 'error', function() {
form.showErrorMessage();
form.hideLoadingIndicator();
});
@@ -89,11 +87,8 @@
}
function quote(string) {
return '"'+string+'"';
return '"' + string + '"';
}
};
});
})(define || RequireJS.define);

View File

@@ -1,26 +1,23 @@
;(function (define) {
(function(define) {
define(['backbone'], function(Backbone) {
'use strict';
define(['backbone'], function (Backbone) {
'use strict';
return Backbone.Model.extend({
defaults: {
modes: [],
course: '',
enrollment_start: '',
number: '',
content: {
overview: '',
display_name: '',
number: ''
},
start: '',
image_url: '',
org: '',
id: ''
}
return Backbone.Model.extend({
defaults: {
modes: [],
course: '',
enrollment_start: '',
number: '',
content: {
overview: '',
display_name: '',
number: ''
},
start: '',
image_url: '',
org: '',
id: ''
}
});
});
});
})(define || RequireJS.define);

Some files were not shown because too many files have changed in this diff Show More