PageRenderTime 68ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/frontend/require/app.js

https://gitlab.com/rohan07/PrairieLearn
JavaScript | 391 lines | 327 code | 42 blank | 22 comment | 18 complexity | 2b533b28d61fbf830c82bdda8f7f46f2 MD5 | raw file
  1. var PRAIRIELEARN_DEFAULT_API_SERVER = "http://localhost:3000";
  2. requirejs.config({
  3. baseUrl: 'require',
  4. paths: {
  5. clientCode: (document.PLConfig.apiServer || PRAIRIELEARN_DEFAULT_API_SERVER) + "/clientCode",
  6. },
  7. map: {
  8. '*': {
  9. 'backbone': 'browser/backbone',
  10. 'underscore': 'browser/underscore',
  11. 'numeric': 'numeric-1.2.6.min'
  12. }
  13. },
  14. waitSeconds: 60,
  15. shim: {
  16. 'numeric-1.2.6.min': {
  17. exports: 'numeric'
  18. },
  19. 'gamma': {
  20. exports: 'module'
  21. },
  22. 'd3': {
  23. exports: 'd3'
  24. },
  25. 'bootstrap' : {
  26. deps: ['jquery'],
  27. exports: 'bootstrap'
  28. },
  29. 'browser/backbone': {
  30. deps: ['underscore', 'jquery'],
  31. exports: 'Backbone'
  32. },
  33. 'browser/underscore': {
  34. exports: '_'
  35. },
  36. 'Tween': {
  37. exports: 'TWEEN'
  38. },
  39. 'jquery.cookie': {
  40. deps: ['jquery']
  41. },
  42. 'sha1': {
  43. exports: 'Sha1',
  44. },
  45. },
  46. config: {
  47. text: {
  48. useXhr: function(url, protocol, hostname, port) {
  49. // see https://github.com/jrburke/requirejs/issues/269
  50. return true;
  51. // return true if you want to allow this url, given that the
  52. // text plugin thinks the request is coming from protocol, hostname, port.
  53. // unilaterally returning true here may mean that html
  54. // files aren't loaded from the optimized
  55. // one-big-js-file
  56. }
  57. },
  58. },
  59. });
  60. requirejs(['jquery', 'jquery.cookie', 'underscore', 'backbone', 'bootstrap', 'mustache', 'NavView', 'HomeView', 'QuestionDataModel', 'QuestionView', 'TestInstanceCollection', 'TestInstanceView', 'TestModel', 'StatsModel', 'StatsView', 'AssessView', 'AboutView', 'spinController'],
  61. function( $, jqueryCookie, _, Backbone, bootstrap, Mustache, NavView, HomeView, QuestionDataModel, QuestionView, TestInstanceCollection, TestInstanceView, TestModel, StatsModel, StatsView, AssessView, AboutView, spinController) {
  62. var QuestionModel = Backbone.Model.extend({
  63. idAttribute: "qid"
  64. });
  65. var QuestionCollection = Backbone.Collection.extend({
  66. model: QuestionModel,
  67. comparator: function(question) {return question.get("number");}
  68. });
  69. var TestCollection = Backbone.Collection.extend({
  70. model: TestModel.TestModel,
  71. comparator: function(test) {return -(new Date(test.get("dueDate") || test.get("availDate"))).getTime();}, // sort by negative time, so later dates first
  72. });
  73. var UserModel = Backbone.Model.extend({
  74. idAttribute: "uid"
  75. });
  76. var UserCollection = Backbone.Collection.extend({
  77. model: UserModel
  78. });
  79. var AppModel = Backbone.Model.extend({
  80. initialize: function() {
  81. defaultConfig = {
  82. page: "home",
  83. currentAssessmentName: null,
  84. currentAssessmentLink: null,
  85. pageOptions: {},
  86. deployMode: false,
  87. apiServer: PRAIRIELEARN_DEFAULT_API_SERVER,
  88. authUID: null,
  89. authName: null,
  90. authDate: null,
  91. authSignature: null,
  92. authPerms: [],
  93. userUID: null,
  94. userName: null,
  95. pageTitle: "PrairieLearn",
  96. navTitle: "PrairieLearn",
  97. authURL: null,
  98. };
  99. this.set(_(document.PLConfig).defaults(defaultConfig));
  100. if (this.get("authURL") === null)
  101. this.set("authURL", this.apiURL("auth"));
  102. document.title = this.get("pageTitle");
  103. var that = this;
  104. $.getJSON(this.get("authURL"), function(data) {
  105. that.set({
  106. authUID: data.uid,
  107. authName: data.name,
  108. authDate: data.date,
  109. authSignature: data.signature,
  110. userUID: data.uid,
  111. userName: data.name
  112. });
  113. $.getJSON(that.apiURL("users/" + that.get("authUID")), function(userData) {
  114. that.set("authPerms", userData.perms);
  115. });
  116. });
  117. this.listenTo(Backbone, "tryAgain", this.tryAgain);
  118. },
  119. apiURL: function(path) {
  120. return this.get("apiServer") + "/" + path;
  121. },
  122. tryAgain: function() {
  123. this.trigger("change");
  124. },
  125. hasPermission: function(operation) {
  126. var perms = this.get("authPerms");
  127. if (!perms)
  128. return false;
  129. var permission = false;
  130. if (operation === "overrideScore" && _(perms).contains("superuser"))
  131. permission = true;
  132. if (operation === "seeQID" && _(perms).contains("superuser"))
  133. permission = true;
  134. if (operation === "changeUser" && _(perms).contains("superuser"))
  135. permission = true;
  136. if (operation === "seeAvailDate" && _(perms).contains("superuser"))
  137. permission = true;
  138. return permission;
  139. }
  140. });
  141. var AppView = Backbone.View.extend({
  142. initialize: function() {
  143. this.router = this.options.router; // hack to enable random question URL re-writing
  144. this.questions = this.options.questions;
  145. this.eScores = this.options.eScores;
  146. this.tests = this.options.tests;
  147. this.tInstances = this.options.tInstances;
  148. this.users = this.options.users;
  149. this.currentView = null;
  150. this.listenTo(this.model, "change", this.render);
  151. this.listenTo(this.model, "change:userUID", this.reloadUserData);
  152. this.navView = new NavView.NavView({model: this.model, users: this.users});
  153. this.navView.render();
  154. $("#nav").html(this.navView.el);
  155. },
  156. render: function() {
  157. var target = document.getElementById('content');
  158. var spinner = spinController.startSpinner(target);
  159. var view;
  160. switch (this.model.get("page")) {
  161. case "home":
  162. view = new HomeView.HomeView({model: this.model});
  163. break;
  164. case "stats":
  165. var statsModel = new StatsModel.StatsModel({}, {appModel: this.model});
  166. view = new StatsView.StatsView({model: statsModel});
  167. break;
  168. case "assess":
  169. view = new AssessView({appModel: this.model, tests: this.tests, tInstances: this.tInstances, router: this.router});
  170. break;
  171. case "testInstance":
  172. var tiid = this.model.get("pageOptions").tiid;
  173. var tInstance = this.tInstances.get(tiid);
  174. var tid = tInstance.get("tid");
  175. var test = this.tests.get(tid);
  176. this.model.set("currentAssessmentName", test.get("set") + " " + test.get("number"));
  177. this.model.set("currentAssessmentLink", "#ti/" + tiid);
  178. view = new TestInstanceView({model: tInstance, test: test, appModel: this.model, questions: this.questions});
  179. break;
  180. case "testQuestion":
  181. var tiid = this.model.get("pageOptions").tiid;
  182. var qNumber = this.model.get("pageOptions").qNumber;
  183. var vid = this.model.get("pageOptions").vid;
  184. var qIndex = qNumber - 1;
  185. var tInstance = this.tInstances.get(tiid);
  186. var tid = tInstance.get("tid");
  187. var test = this.tests.get(tid);
  188. this.model.set("currentAssessmentName", test.get("set") + " " + test.get("number"));
  189. this.model.set("currentAssessmentLink", "#ti/" + tiid);
  190. var qid;
  191. if (tInstance.has("qids"))
  192. qid = tInstance.get("qids")[qIndex];
  193. else
  194. qid = test.get("qids")[qIndex];
  195. var questionDataModel = new QuestionDataModel.QuestionDataModel({}, {appModel: this.model, qid: qid, vid: vid, tInstance: tInstance, test: test});
  196. /*
  197. test.callWithHelper(function() {
  198. var helper = test.get("helper");
  199. if (helper.adjustQuestionDataModel)
  200. helper.adjustQuestionDataModel(questionDataModel, test, tInstance);
  201. });
  202. */
  203. view = new QuestionView.QuestionView({model: questionDataModel, test: test, tInstance: tInstance, appModel: this.model});
  204. break;
  205. /**
  206. * Chooses a question from an active test, excluding any questions in skipQNumbers.
  207. */
  208. case "chooseTestQuestion":
  209. var tiid = this.model.get("pageOptions").tiid;
  210. var qInfo = this.model.get("pageOptions").qInfo;
  211. var skipQNumbers = this.model.get("pageOptions").skipQNumbers;
  212. var tInstance = this.tInstances.get(tiid);
  213. var tid = tInstance.get("tid");
  214. var test = this.tests.get(tid);
  215. this.model.set("currentAssessmentName", test.get("set") + " " + test.get("number"));
  216. this.model.set("currentAssessmentLink", "#ti/" + tiid);
  217. var qids;
  218. if (tInstance.has("qids"))
  219. qids = tInstance.get("qids");
  220. else
  221. qids = test.get("qids");
  222. // skipQNumbers is an array of strings, containing a question number (starting from question 1, not zero-based)
  223. // ...we'll map them to integers
  224. var skipQuestionNumbers = _(skipQNumbers).map(function (s) { return parseInt(s); });
  225. // fill an array with all question numbers (from qids)
  226. var allQuestionNumbers = _.range(1, qids.length + 1);
  227. // remove the skipped questions from the list of all questions
  228. var remainingQuestionNumbers = _(allQuestionNumbers).difference(skipQuestionNumbers);
  229. // randomly choose a next question from the list of remaining questions
  230. var chosenQuestionNumber = _.sample(remainingQuestionNumbers);
  231. // navigate the page to the new question
  232. this.router.navigate("q/" + tiid + "/" + chosenQuestionNumber, true);
  233. return;
  234. case "about":
  235. view = new AboutView.AboutView();
  236. break;
  237. }
  238. this.showView(view);
  239. spinController.stopSpinner(spinner);
  240. },
  241. showView: function(view) {
  242. if (this.currentView != null) {
  243. this.currentView.close();
  244. }
  245. this.currentView = view;
  246. view.render();
  247. $("#content").html(view.el);
  248. $('[data-toggle=tooltip]').tooltip();
  249. },
  250. reloadUserData: function() {
  251. this.tInstances.fetch({reset: true});
  252. }
  253. });
  254. var AppRouter = Backbone.Router.extend({
  255. routes: {
  256. "stats": "goStats",
  257. "assess": "goAssess",
  258. "q/:tiid/:qNumber(/:vid)": "goTestQuestion",
  259. "cq/:tiid/:qInfo(/not/:skipQNumbers)": "goChooseTestQuestion",
  260. "ti/:tiid": "goTestInstance",
  261. "about": "goAbout",
  262. "*actions": "goHome"
  263. },
  264. initialize: function(options) {
  265. this.model = options.model;
  266. },
  267. goHome: function(actions) {
  268. this.model.set({
  269. "page": "assess",
  270. "pageOptions": {}
  271. });
  272. },
  273. goStats: function() {
  274. this.model.set({
  275. "page": "stats",
  276. "pageOptions": {}
  277. });
  278. },
  279. goAssess: function() {
  280. this.model.set({
  281. "page": "assess",
  282. "pageOptions": {}
  283. });
  284. },
  285. goTestQuestion: function(tiid, qNumber, vid) {
  286. this.model.set({
  287. "page": "testQuestion",
  288. "pageOptions": {tiid: tiid, qNumber: qNumber, vid: vid}
  289. });
  290. },
  291. goChooseTestQuestion: function(tiid, qInfo, skipQNumbers) {
  292. skipQNumbers = (skipQNumbers == null) ? [] : skipQNumbers.split(",");
  293. this.model.set({
  294. "page": "chooseTestQuestion",
  295. "pageOptions": {tiid: tiid, qInfo: qInfo, skipQNumbers: skipQNumbers}
  296. });
  297. },
  298. goTestInstance: function(tiid) {
  299. this.model.set({
  300. "page": "testInstance",
  301. "pageOptions": {tiid: tiid}
  302. });
  303. },
  304. goAbout: function() {
  305. this.model.set({
  306. "page": "about",
  307. "pageOptions": {}
  308. });
  309. },
  310. });
  311. $(function() {
  312. var appModel = new AppModel();
  313. var appRouter = new AppRouter({model: appModel});
  314. var questions = new QuestionCollection([], {
  315. url: function() {return appModel.apiURL("questions");}
  316. });
  317. var tests = new TestCollection([], {
  318. url: function() {return appModel.apiURL("tests");}
  319. });
  320. var tInstances = new TestInstanceCollection.TestInstanceCollection([], {
  321. tests: tests,
  322. url: function() {return appModel.apiURL("tInstances/?uid=" + appModel.get("userUID"));}
  323. });
  324. var users = new UserCollection([], {
  325. url: function() {return appModel.apiURL("users");}
  326. });
  327. Backbone.history.start();
  328. $.ajaxPrefilter(function(options, originalOptions, jqXHR) {
  329. options.headers = {
  330. "X-Auth-UID": String(appModel.get("authUID")),
  331. "X-Auth-Name": String(appModel.get("authName")),
  332. "X-Auth-Date": String(appModel.get("authDate")),
  333. "X-Auth-Signature": String(appModel.get("authSignature")),
  334. };
  335. });
  336. appModel.on("change:authUID", function() {
  337. questions.fetch({success: function() {
  338. tests.fetch({success: function() {
  339. tInstances.fetch({success: function() {
  340. users.fetch({success: function() {
  341. var appView = new AppView({model: appModel, questions: questions, tests: tests, tInstances: tInstances, router: appRouter, users: users});
  342. appView.render();
  343. }});
  344. }});
  345. }});
  346. }});
  347. });
  348. });
  349. });