Merge pull request #13172 from edx/bjacobel/autofix-lms
Run ESLint autofixer in LMS
This commit is contained in:
@@ -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')
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, '');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(','),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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: '',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
})
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, """));
|
||||
g_titles.set(m1, m5.replace(/"/g, '"'));
|
||||
}
|
||||
|
||||
// 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, ">").replace(/</g, "<").replace(/"/g, """);
|
||||
return text.replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
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, "&");
|
||||
text = text.replace(/&/g, '&');
|
||||
|
||||
// Do the angle bracket song and dance:
|
||||
text = text.replace(/</g, "<");
|
||||
text = text.replace(/>/g, ">");
|
||||
text = text.replace(/</g, '<');
|
||||
text = text.replace(/>/g, '>');
|
||||
|
||||
// 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, "&");
|
||||
text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&');
|
||||
|
||||
// Encode naked <'s
|
||||
text = text.replace(/<(?![a-z\/?\$!])/gi, "<");
|
||||
text = text.replace(/<(?![a-z\/?\$!])/gi, '<');
|
||||
|
||||
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
|
||||
})();
|
||||
|
||||
@@ -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, "(").replace(/\)/g, ")").replace(/</g, "<").replace(/>/g, ">");
|
||||
title = title.trim ? title.trim() : title.replace(/^\s*/, '').replace(/\s*$/, '');
|
||||
title = $.trim(title).replace(/"/g, 'quot;').replace(/\(/g, '(').replace(/\)/g, ')').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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>');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -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.')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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: '',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', {});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user