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

/t/upfront/scripts/upfront/upfront-models.js

https://bitbucket.org/matthewselby/wpdev
JavaScript | 1756 lines | 1401 code | 175 blank | 180 comment | 308 complexity | 62ff7df6c62c95341cd5d89aad6b37ad MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, LGPL-3.0, LGPL-2.1, AGPL-1.0, BSD-3-Clause, MIT, GPL-3.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. (function ($) {
  2. define(['backbone'], function(Backbone) {
  3. Upfront.Events = _.isEmpty(Upfront.Events) ? _.extend(Upfront.Events, Backbone.Events) : Upfront.Events;
  4. var _alpha = "alpha",
  5. /* ----- Logic mixins ----- */
  6. _Upfront_ModelMixin = {
  7. },
  8. /* ----- Core model definitions ----- */
  9. // Basic behavior/appearance dataset building block
  10. Property = Backbone.Model.extend({
  11. "defaults": {
  12. "name": "",
  13. "value": ""
  14. },
  15. idAttribute: 'name'
  16. }),
  17. // Basic behavior/appearance dataset
  18. Properties = Backbone.Collection.extend({
  19. "model": Property
  20. }),
  21. // Basic interface dataset container
  22. ObjectModel = Backbone.Model.extend({
  23. "defaults": {
  24. "name": "",
  25. "element_id": "",
  26. "properties": new Properties()
  27. },
  28. initialize: function () {
  29. var args = arguments;
  30. if (args && args[0] && args[0]["properties"]) {
  31. args[0]["properties"] = args[0]["properties"] instanceof Properties
  32. ? args[0]["properties"]
  33. : new Properties(args[0]["properties"])
  34. ;
  35. this.set("properties", args[0].properties);
  36. } else this.set("properties", new Properties([]));
  37. if (this.init) this.init();
  38. // Take care of old preset API
  39. if (this.get_property_value_by_name('currentpreset')) {
  40. // Unset currentpreset property and set preset to correct value
  41. this.set_property('preset', this.get_property_value_by_name('currentpreset'), true);
  42. this.set_property('currentpreset', false, true);
  43. }
  44. },
  45. // ----- Object interface ----- */
  46. get_view_class: function () {
  47. return Upfront.Views.ObjectView;
  48. },
  49. get_property_by_name: function (name) {
  50. var prop = this.get("properties").where({"name": name});
  51. return prop.length ? prop[0] : false;
  52. },
  53. get_property_value_by_name: function (name) {
  54. var prop = this.get_property_by_name(name);
  55. return prop && prop.get ? prop.get("value") : false;
  56. },
  57. has_property: function (name) {
  58. return !!this.get_property_value_by_name(name);
  59. },
  60. has_property_value: function (property, value) {
  61. return (value == this.get_property_value_by_name(property));
  62. },
  63. /**
  64. * Resolve the preset value from wherever we might be having it stored
  65. *
  66. * Resolves the preset and, as a side-effect, sets the `preset` property
  67. * to the resolved value.
  68. * This way the `preset` property is now more of a transient, contextyally
  69. * dependent value - not fixed and given once by the mighty hand of god.
  70. *
  71. * The value is resolved by first checking the passed breakpoint ID
  72. * (which will default to currently active one) and, if that fails,
  73. * will default to whatever the `preset` property says it should be.
  74. * Failing all of that, it'll fall back to "default"
  75. *
  76. * @param {String} breakpoint_id Breakpoint ID used to resolve the preset from storage
  77. * * - will default to current one
  78. *
  79. * @return {String} Decoded preset ID
  80. */
  81. decode_preset: function (breakpoint_id) {
  82. breakpoint_id = breakpoint_id || (Upfront.Views.breakpoints_storage.get_breakpoints().get_active() || {}).id;
  83. var current = this.get_property_value_by_name('current_preset') || this.get_property_value_by_name('preset') || 'default',
  84. model = this.get_property_value_by_name("breakpoint_presets") || {},
  85. breakpoint_preset
  86. ;
  87. // we need to provide proper fallback here, mobile -> tablet -> desktop
  88. if ( breakpoint_id == 'mobile' ) {
  89. breakpoint_preset = (model[breakpoint_id] || model['tablet'] || model['desktop'] || {}).preset;
  90. } else if ( breakpoint_id == 'tablet' ) {
  91. breakpoint_preset = (model[breakpoint_id] || model['desktop'] || {}).preset;
  92. } else {
  93. breakpoint_preset = (model[breakpoint_id] || {}).preset;
  94. // when on desktop, set `current_preset` to desktop preset
  95. current = breakpoint_preset || current || 'default';
  96. }
  97. var actual = breakpoint_preset || current;
  98. // we have to retain current preset coz will be lose below
  99. this.set_property('current_preset', current, true);
  100. // this will repaint the element but will also lose our current preset
  101. this.set_property('preset', actual, false); // Do *not* be silent here, we do want repaint
  102. return actual;
  103. },
  104. /**
  105. * Pack up the breakpoint preset values.
  106. *
  107. * The packed values will be decoded later on using the `decode_preset` method.
  108. * As a side-effect, we also update the model `breakpoint_presets` property.
  109. * As a side-effect #2, we also set whatever the current preset is (or default) as
  110. * default breakpoint preset, if it's not already set.
  111. *
  112. * @param {String} preset_id Preset ID to pack
  113. * @param {String} breakpoint_id Breakpoint ID used to resolve the preset in storage
  114. * - will default to current one
  115. *
  116. * @return {Object} Packed breakpoint presets
  117. */
  118. encode_preset: function (preset_id, breakpoint_id) {
  119. breakpoint_id = breakpoint_id || (Upfront.Views.breakpoints_storage.get_breakpoints().get_active() || {}).id;
  120. var data = this.get_property_value_by_name("breakpoint_presets") || {},
  121. current = (this.get_property_by_name('preset').previousAttributes() || {value: 'default'}).value,
  122. default_bp_id = (Upfront.Views.breakpoints_storage.get_breakpoints().findWhere({'default': true}) || {}).id
  123. ;
  124. data[breakpoint_id] = {preset: preset_id};
  125. if (!data[default_bp_id]) data[default_bp_id] = {preset: current};
  126. this.set_property("breakpoint_presets", data, true);
  127. return data;
  128. },
  129. add_property: function (name, value, silent) {
  130. if (!silent) silent = false;
  131. this.get("properties").add(new Upfront.Models.Property({"name": name, "value": value}), {"silent": silent});
  132. Upfront.Events.trigger("model:property:add", name, value, silent);
  133. },
  134. set_property: function (name, value, silent) {
  135. if (!name) return false;
  136. if (!silent) silent = false;
  137. var prop = this.get_property_by_name(name);
  138. if (!prop || !prop.set) return this.add_property(name, value, silent);
  139. prop.set({"value": value}, {"silent": silent});
  140. Upfront.Events.trigger("model:property:set", name, value, silent);
  141. },
  142. remove_property: function (name, silent) {
  143. if (!name) return false;
  144. if (!silent) silent = false;
  145. var prop = this.get_property_by_name(name);
  146. if (!prop || !prop.set) return;
  147. this.get("properties").remove(prop, {"silent": silent});
  148. Upfront.Events.trigger("model:property:remove", name, silent);
  149. },
  150. init_property: function (name, value) {
  151. if (!this.has_property(name)) this.add_property(name, value);
  152. },
  153. init_properties: function (hash) {
  154. var me = this;
  155. _(hash).each(function (value, name) {
  156. me.init_property(name, value);
  157. });
  158. },
  159. // ----- Magic properties manipulation ----- */
  160. get_content: function () {
  161. return this.get_property_value_by_name("content");
  162. },
  163. set_content: function (content, options) {
  164. options = typeof options != 'undefined' ? options: {};
  165. var prop = this.get_property_by_name("content");
  166. if (prop) return prop.set("value", content, options);
  167. return this.get("properties").add(new Upfront.Models.Property({"name": "content", "value": content}));
  168. },
  169. get_element_id: function () {
  170. return this.get_property_value_by_name("element_id");
  171. },
  172. get_wrapper_id: function () {
  173. return this.get_property_value_by_name("wrapper_id");
  174. },
  175. replace_class: function (value) {
  176. var prop = this.get_property_by_name("class"),
  177. old = prop ? prop.get("value") : false
  178. ;
  179. if (prop && old){
  180. // Have class
  181. var values = value.split(" "),
  182. new_val = old;
  183. for ( var i = 0; i < values.length; i++ ){
  184. var val_esc = values[i].replace(/-?\d+/, '-?\\d+'),
  185. val_rx = new RegExp(val_esc);
  186. if ( new_val.match(val_rx) )
  187. new_val = new_val.replace(val_rx, values[i]);
  188. else
  189. new_val += " " + values[i];
  190. }
  191. return prop.set("value", new_val);
  192. }
  193. else if (!prop) this.get("properties").add(new Property({"name": "class", "value": value})); // No class property
  194. return false;
  195. },
  196. add_class: function (value) {
  197. var val_rx = new RegExp(value),
  198. prop = this.get_property_by_name("class"),
  199. old = prop ? prop.get("value") : false;
  200. if (prop && !old.match(val_rx)) return prop.set("value", old + " " + value);
  201. else if (!prop) this.get("properties").add(new Property({"name": "class", "value": value}));
  202. return false;
  203. },
  204. remove_class: function (value) {
  205. var val_rx = new RegExp(value),
  206. prop = this.get_property_by_name("class"),
  207. old = prop ? prop.get("value") : false;
  208. if (prop && old.match(val_rx)) return prop.set("value", old.replace(val_rx, ""));
  209. return false;
  210. },
  211. is_visible: function () {
  212. return this.get_property_value_by_name("visibility");
  213. },
  214. get_breakpoint_property_value: function (property, return_default, default_value, breakpoint) {
  215. breakpoint = breakpoint ? breakpoint : Upfront.Views.breakpoints_storage.get_breakpoints().get_active().toJSON();
  216. default_value = typeof default_value === "undefined" ? false : default_value;
  217. if ( !breakpoint || breakpoint['default'] )
  218. return this.get_property_value_by_name(property);
  219. var data = this.get_property_value_by_name('breakpoint');
  220. if ( _.isObject(data) && _.isObject(data[breakpoint.id]) && property in data[breakpoint.id] )
  221. return data[breakpoint.id][property];
  222. if ( return_default )
  223. return this.get_property_value_by_name(property);
  224. return default_value;
  225. },
  226. set_breakpoint_property: function (property, value, silent, breakpoint) {
  227. breakpoint = breakpoint ? breakpoint : Upfront.Views.breakpoints_storage.get_breakpoints().get_active().toJSON();
  228. if ( !breakpoint || breakpoint['default'] ) {
  229. this.set_property(property, value, silent);
  230. }
  231. else {
  232. var data = Upfront.Util.clone(this.get_property_value_by_name('breakpoint') || {});
  233. if ( !_.isObject(data[breakpoint.id]) )
  234. data[breakpoint.id] = {};
  235. data[breakpoint.id][property] = value;
  236. data.current_property = property;
  237. this.set_property('breakpoint', data, silent);
  238. }
  239. },
  240. add_to: function (collection, index, options) {
  241. options = _.isObject(options) ? options : {};
  242. var me = this,
  243. models = [],
  244. added = false
  245. ;
  246. collection.each(function(each, i){
  247. if ( i == index ){
  248. models.push(me);
  249. added = true;
  250. }
  251. models.push(each);
  252. });
  253. if ( added ){
  254. collection.reset(models, {silent: true});
  255. collection.trigger('add', this, collection, _.extend(options, {index: index}));
  256. }
  257. else {
  258. collection.add(this, _.extend(options, {index: index}));
  259. }
  260. }
  261. }),
  262. ObjectGroup = ObjectModel.extend({
  263. "defaults": function(){
  264. return {
  265. "name": "",
  266. "objects": new Objects(),
  267. "wrappers": new Wrappers(),
  268. "properties": new Properties()
  269. };
  270. },
  271. initialize: function () {
  272. var args = arguments;
  273. if (args && args[0] && args[0]["objects"]) {
  274. args[0]["objects"] = args[0]["objects"] instanceof Objects
  275. ? args[0]["objects"]
  276. : new Objects(args[0]["objects"])
  277. ;
  278. this.set("objects", args[0]["objects"]);
  279. } else this.set("objects", new Objects([]));
  280. if (args && args[0] && args[0]["wrappers"]) {
  281. args[0]["wrappers"] = args[0]["wrappers"] instanceof Wrappers
  282. ? args[0]["wrappers"]
  283. : new Wrappers(args[0]["wrappers"])
  284. ;
  285. this.set("wrappers", args[0].wrappers);
  286. } else this.set("wrappers", new Wrappers([]));
  287. if (args && args[0] && args[0]["properties"]) {
  288. args[0]["properties"] = args[0]["properties"] instanceof Properties
  289. ? args[0]["properties"]
  290. : new Properties(args[0]["properties"])
  291. ;
  292. this.set("properties", args[0]["properties"]);
  293. } else this.set("properties", new Properties([]));
  294. if (this.init) this.init();
  295. }
  296. }),
  297. // Basic interface dataset
  298. Objects = Backbone.Collection.extend({
  299. model: function (attrs, options) {
  300. var type_prop = attrs["properties"] ? _(attrs["properties"]).where({"name": "type"}) : attrs.get("properties").where({"name": "type"}),
  301. type = type_prop.length ? type_prop[0].value : '';
  302. if (Upfront.Models[type]) return new Upfront.Models[type](attrs, options);
  303. return new ObjectModel(attrs, options);
  304. },
  305. get_by_element_id: function (element_id) {
  306. var found = false;
  307. this.each(function (model) {
  308. if (model.get_element_id() == element_id) found = model;
  309. });
  310. return found;
  311. }
  312. }),
  313. // Module interface dataset container
  314. Module = ObjectModel.extend({
  315. "defaults": {
  316. "name": "",
  317. "objects": new Objects(),
  318. "properties": new Properties()
  319. },
  320. initialize: function () {
  321. var args = arguments;
  322. if (args && args[0] && args[0]["objects"]) {
  323. args[0]["objects"] = args[0]["objects"] instanceof Objects
  324. ? args[0]["objects"]
  325. : new Objects(args[0]["objects"])
  326. ;
  327. this.set("objects", args[0].objects);
  328. } else this.set("objects", new Objects([]));
  329. if (args && args[0] && args[0]["properties"]) {
  330. args[0]["properties"] = args[0]["properties"] instanceof Properties
  331. ? args[0]["properties"]
  332. : new Properties(args[0]["properties"])
  333. ;
  334. this.set("properties", args[0]["properties"]);
  335. } else this.set("properties", new Properties([]));
  336. if (this.init) this.init();
  337. }
  338. }),
  339. ModuleGroup = ObjectModel.extend({
  340. "defaults": function(){
  341. return {
  342. "name": "",
  343. "modules": new Modules(),
  344. "wrappers": new Wrappers(),
  345. "properties": new Properties()
  346. };
  347. },
  348. initialize: function () {
  349. var args = arguments;
  350. if (args && args[0] && args[0]["modules"]) {
  351. args[0]["modules"] = args[0]["modules"] instanceof Modules
  352. ? args[0]["modules"]
  353. : new Modules(args[0]["modules"])
  354. ;
  355. this.set("modules", args[0]["modules"]);
  356. } else this.set("modules", new Modules([]));
  357. if (args && args[0] && args[0]["wrappers"]) {
  358. args[0]["wrappers"] = args[0]["wrappers"] instanceof Wrappers
  359. ? args[0]["wrappers"]
  360. : new Wrappers(args[0]["wrappers"])
  361. ;
  362. this.set("wrappers", args[0].wrappers);
  363. } else this.set("wrappers", new Wrappers([]));
  364. if (args && args[0] && args[0]["properties"]) {
  365. args[0]["properties"] = args[0]["properties"] instanceof Properties
  366. ? args[0]["properties"]
  367. : new Properties(args[0]["properties"])
  368. ;
  369. this.set("properties", args[0]["properties"]);
  370. } else this.set("properties", new Properties([]));
  371. this.init_property('has_settings', 1);
  372. this.init_property('type', 'ModuleGroup');
  373. if (this.init) this.init();
  374. }
  375. }),
  376. Modules = Backbone.Collection.extend({
  377. model: function (attrs, options) {
  378. if ( attrs.modules )
  379. return new ModuleGroup(attrs, options);
  380. return new Module(attrs, options);
  381. },
  382. get_by_element_id: function (element_id) {
  383. var found = false;
  384. this.each(function (model) {
  385. if (model.get_element_id() == element_id) found = model;
  386. });
  387. return found;
  388. }
  389. }),
  390. Region = ObjectModel.extend({
  391. defaults: function(){
  392. return {
  393. "name": "",
  394. "properties": new Properties(),
  395. "wrappers": new Wrappers(),
  396. "modules": new Modules()
  397. };
  398. },
  399. initialize: function () {
  400. var args = arguments;
  401. if (args && args[0] && args[0]["modules"]) {
  402. args[0]["modules"] = args[0]["modules"] instanceof Modules
  403. ? args[0]["modules"]
  404. : new Modules(args[0]["modules"])
  405. ;
  406. this.set("modules", args[0].modules);
  407. } else this.set("modules", new Modules([]));
  408. if (args && args[0] && args[0]["wrappers"]) {
  409. args[0]["wrappers"] = args[0]["wrappers"] instanceof Wrappers
  410. ? args[0]["wrappers"]
  411. : new Wrappers(args[0]["wrappers"])
  412. ;
  413. this.set("wrappers", args[0].wrappers);
  414. } else this.set("wrappers", new Wrappers([]));
  415. if (args && args[0] && args[0]["properties"]) {
  416. args[0]["properties"] = args[0]["properties"] instanceof Properties
  417. ? args[0]["properties"]
  418. : new Properties(args[0]["properties"])
  419. ;
  420. this.set("properties", args[0].properties);
  421. } else this.set("properties", new Properties([]));
  422. },
  423. is_main: function () {
  424. var container = this.get('container'),
  425. name = this.get('name');
  426. return ( !container || container == name );
  427. },
  428. get_sub_regions: function () {
  429. if ( ! this.collection )
  430. return false;
  431. var collection = this.collection,
  432. index = collection.indexOf(this),
  433. total = collection.size()-1, // total minus shadow region
  434. container = this.get('container') || this.get('name'),
  435. ref_models = collection.filter(function(model){ return model.get('container') == container || model.get('name') == container; }),
  436. ref_models2 = [],
  437. ret = {
  438. fixed: [],
  439. lightbox: [],
  440. top: false,
  441. left: false,
  442. right: false,
  443. bottom: false
  444. };
  445. if ( ref_models.length > 1 ){
  446. _.each(ref_models, function(model, index){
  447. var sub = model.get('sub');
  448. if ( sub == 'fixed' )
  449. ret.fixed.push(model);
  450. else if ( sub == 'lightbox' )
  451. ret.lightbox.push(model);
  452. else if ( sub && sub.match(/^(top|left|bottom|right)$/) )
  453. ret[sub] = model;
  454. else
  455. ref_models2.push(model);
  456. });
  457. }
  458. if ( ref_models2.length > 1 ){
  459. var _index = _.indexOf(ref_models2, this);
  460. if ( _index === 0 )
  461. ret.right = ref_models2[1];
  462. else if ( _index === 1 ){
  463. ret.left = ref_models2[0];
  464. ret.right = ref_models2.length > 2 ? ref_models2[2] : false;
  465. }
  466. }
  467. return ret;
  468. },
  469. get_sub_region: function (sub) {
  470. return this.get_sub_regions()[sub];
  471. },
  472. has_sub_region: function () {
  473. return _.find( this.get_sub_regions(), function(each){ return ( each !== false ); } );
  474. },
  475. get_side_region: function (right) {
  476. return this.get_sub_region( right ? 'right' : 'left' );
  477. },
  478. has_side_region: function () {
  479. var sub = this.get_sub_regions();
  480. return ( sub.left || sub.right );
  481. }
  482. }),
  483. Regions = Backbone.Collection.extend({
  484. "model": Region,
  485. get_by_name: function (name) {
  486. name = name.toLowerCase();
  487. var found = false;
  488. this.each(function (model) {
  489. if (model.get("name").toLowerCase() == name) found = model;
  490. });
  491. return found;
  492. },
  493. at_container: function (index) {
  494. var i = 0;
  495. return this.find(function(m){
  496. if ( m.is_main() ){
  497. if ( i == index )
  498. return true;
  499. else
  500. i++;
  501. }
  502. return false;
  503. });
  504. },
  505. index_container: function (model, excludes) {
  506. excludes = _.isArray(excludes) ? excludes : [excludes];
  507. var collection = this.filter(function(m){
  508. return m.is_main() && ! _.contains(excludes, m.get('name'));
  509. }),
  510. index = collection.indexOf(model);
  511. return index;
  512. },
  513. total_container: function (excludes) {
  514. excludes = _.isArray(excludes) ? excludes : [excludes];
  515. var collection = this.filter(function(m){
  516. return m.is_main() && ! _.contains(excludes, m.get('name'));
  517. });
  518. return collection.length;
  519. },
  520. get_new_title: function (prefix, start) {
  521. var title = (prefix + start).replace(/[^A-Za-z0-9\s_-]/g, ''),
  522. name = title.toLowerCase().replace(/\s/g, '-');
  523. if ( this.get_by_name(name) )
  524. return this.get_new_title(prefix, start+1);
  525. return {
  526. title: title,
  527. name: name
  528. };
  529. }
  530. }),
  531. Wrapper = ObjectModel.extend({
  532. "defaults": {
  533. "name": "",
  534. "properties": new Properties()
  535. },
  536. initialize: function () {
  537. var args = arguments;
  538. if (args && args[0] && args[0]["properties"]) {
  539. args[0]["properties"] = args[0]["properties"] instanceof Properties
  540. ? args[0]["properties"]
  541. : new Properties(args[0]["properties"])
  542. ;
  543. this.set("properties", args[0].properties);
  544. } else this.set("properties", new Properties([]));
  545. }
  546. }),
  547. Wrappers = Backbone.Collection.extend({
  548. "model": Wrapper,
  549. get_by_wrapper_id: function (wrapper_id) {
  550. var found = false;
  551. this.each(function (model) {
  552. if (model.get_wrapper_id() == wrapper_id) found = model;
  553. });
  554. return found;
  555. }
  556. }),
  557. Layout = ObjectModel.extend({
  558. "defaults": {
  559. "name": "",
  560. "properties": new Properties(),
  561. "regions": new Regions(),
  562. "wrappers": new Wrappers()
  563. },
  564. initialize: function () {
  565. var typography;
  566. var args = arguments;
  567. if (args && args[0] && args[0]["regions"]) {
  568. args[0]["regions"] = args[0]["regions"] instanceof Regions
  569. ? args[0]["regions"]
  570. : new Regions(args[0]["regions"])
  571. ;
  572. this.set("regions", args[0].regions);
  573. }
  574. if (args && args[0] && args[0]["properties"]) {
  575. args[0]["properties"] = args[0]["properties"] instanceof Properties
  576. ? args[0]["properties"]
  577. : new Properties(args[0]["properties"])
  578. ;
  579. this.set("properties", args[0].properties);
  580. }
  581. if (args && args[0] && args[0]["wrappers"]) {
  582. args[0]["wrappers"] = args[0]["wrappers"] instanceof Wrappers
  583. ? args[0]["wrappers"]
  584. : new Wrappers(args[0]["wrappers"])
  585. ;
  586. this.set("wrappers", args[0].wrappers);
  587. }
  588. },
  589. get_current_state: function () {
  590. return Upfront.PreviewUpdate.get_revision();
  591. },
  592. has_undo_states: function () {
  593. return !!Upfront.Util.Transient.length("undo");
  594. },
  595. has_redo_states: function () {
  596. return !!Upfront.Util.Transient.length("redo");
  597. },
  598. store_undo_state: function () {
  599. var state = this.get_current_state(),
  600. all = Upfront.Util.Transient.get_all()
  601. ;
  602. if (all.indexOf(state) >= 0) return false;
  603. Upfront.Util.Transient.push("undo", state);
  604. Upfront.Events.trigger("upfront:undo:state_stored");
  605. },
  606. restore_undo_state: function () {
  607. if (!this.has_undo_states()) return false;
  608. return this.restore_state_from_stack("undo");
  609. },
  610. restore_redo_state: function () {
  611. if (!this.has_redo_states()) return false;
  612. return this.restore_state_from_stack("redo");
  613. },
  614. restore_state_from_stack: function (stack) {
  615. var other = ("undo" == stack ? "redo" : "undo"),
  616. revision = Upfront.Util.Transient.pop(stack)
  617. ;
  618. if (!revision) {
  619. Upfront.Util.log("Invalid " + revision + " state");
  620. return false;
  621. }
  622. Upfront.Util.Transient.push(other, this.get_current_state());
  623. // ... 1. get the state that corresponds to this revision
  624. var me = this,
  625. dfr = new $.Deferred()
  626. ;
  627. Upfront.Util.post({
  628. action: 'upfront_get_revision',
  629. revision: revision
  630. }).done(function (response) {
  631. if ("revision" in response.data) {
  632. // ... 2. do this:
  633. me.get("regions").reset(Upfront.Util.model_to_json(response.data.revision.regions));
  634. dfr.resolve();
  635. }
  636. });
  637. return dfr.promise();
  638. }
  639. }),
  640. Taxonomy = Backbone.Model.extend({
  641. initialize: function () {
  642. var args = arguments,
  643. data = args[0] || {}
  644. ;
  645. this.taxonomy = data.taxonomy ? new Backbone.Model(data.taxonomy) : Backbone.Model({});
  646. this.all_terms = data.all_terms ? new Backbone.Collection(data.all_terms) : new Backbone.Collection([]);
  647. this.post_terms = data.post_terms ? new Backbone.Collection(data.post_terms) : new Backbone.Collection([]);
  648. }
  649. }),
  650. /**
  651. * Represents a WP object. Extending WPModel it is easy to communicate with the server
  652. * to fetch a Post, User or Comment, and let the user update them easily.
  653. */
  654. WPModel = Backbone.Model.extend({
  655. action: 'upfront-wp-model',
  656. fetchAttributes: [],
  657. saveAttributes: [],
  658. /**
  659. * Loads the model from the db data. Uses the attribute modelName, implemented in a children class, to
  660. * know what to fecth. When finished it trigger the change event if there have been any change in the Model.
  661. * @param {Object} data Aditional data to be sent with the fetch request.
  662. * @return {jQuery.Deferred} A promise for the fetching. The server response will be passed as argument to the done function.
  663. */
  664. fetch: function(data) {
  665. var me = this;
  666. postdata = {
  667. action: 'fetch_' + this.modelName,
  668. id: this.id
  669. }
  670. ;
  671. _.each(this.fetchAttributes, function(attr){
  672. postdata[attr] = me[attr];
  673. });
  674. postdata = _.extend(postdata, data);
  675. return this.post(postdata)
  676. .done(function(response){
  677. me.set(response.data);
  678. }
  679. );
  680. },
  681. /**
  682. * Update, or create if no model id given, the model in the database.
  683. * Uses the attribute modelName, implemented in a children class, to
  684. * know what to save.
  685. * @return {jQuery.Deferred} A promise for the saving. The server response will be passed as argument to the done function.
  686. */
  687. save: function(){
  688. var me = this,
  689. data = this.toJSON()
  690. ;
  691. _.each(this.saveAttributes, function(attr){
  692. data[attr] = me[attr];
  693. });
  694. data.action = 'save_' + this.modelName;
  695. return this.post(data)
  696. .done(function(response){
  697. me.changed = {};
  698. }
  699. );
  700. },
  701. /**
  702. * Send a POST request to the server setting all the parameters needed to communicate with
  703. * the models endpoint.
  704. * @param {Object} data Data to be sent with the request.
  705. * @return {jQuery.Deferred} A promise for the response. The server response will be passed as argument to the done function.
  706. */
  707. post: function(data){
  708. data = _.isObject(data) ? _.clone(data) : {};
  709. data.model_action = data.action;
  710. data.action = this.action;
  711. return Upfront.Util.post(data);
  712. },
  713. /**
  714. * Overrides Backbone.Model.get to convert the PHP dates in javascript ones.
  715. * @param {String} attr The attribute name to get.
  716. * @return {Mixed} The attribute value or false if not found.
  717. */
  718. get: function(attr){
  719. var value = this.attributes[attr],
  720. dates = [
  721. "post_date",
  722. "post_date_gmt",
  723. 'post_modified',
  724. "post_modified_gmt"
  725. ];
  726. if( _.indexOf(dates, attr) !== -1 ){
  727. //return new Date( value ); // <-- Breaks in FF
  728. var raw_offset = (new Date()).getTimezoneOffset(),
  729. tz_offset = raw_offset / 60,
  730. offset = tz_offset > 0 ? '-' : '+', // Reversed because Date.getTimezoneOffset() returns reversed values...
  731. hours = parseInt(Math.abs(tz_offset), 10),
  732. mins = parseInt((Math.abs(tz_offset) - hours) * 60, 10),
  733. timestamp = value.replace(/ /, 'T')
  734. ;
  735. hours = hours >= 10 ? '' + hours : '0' + hours;
  736. mins = mins >= 10 ? '' + mins : '0' + mins;
  737. if (timestamp && hours.length && mins.length) timestamp += offset + hours + mins;
  738. // Have to do this in order to satisfy safari as well.
  739. // This works with Firefox and chrome too.
  740. var a = timestamp.split(/[^0-9]/);
  741. return new Date (a[0],a[1]-1,a[2],a[3],a[4],a[5]);
  742. }
  743. return this.attributes[attr];
  744. },
  745. /**
  746. * Overrides Backbone.Model.set to convert javascript dates in PHP format.
  747. * @param {String} key Attribute name
  748. * @param {Mixed} val The value for the attribute
  749. * @param {Object} options Extended options for set. See http://backbonejs.org/#Model-set
  750. * @return {WPModel} This object
  751. */
  752. set: function(key, val, options){
  753. var newval = val,
  754. parsedAttrs = {};
  755. if (_.isObject(key)) {
  756. for (var attr in key) {
  757. var value = key[attr];
  758. if (val instanceof Date)
  759. parsedAttrs[attr] = Upfront.Util.format_date(value, true, true).replace(/\//g, '-');
  760. else
  761. parsedAttrs[attr] = value;
  762. }
  763. Backbone.Model.prototype.set.call(this, parsedAttrs, options);
  764. } else {
  765. if (val instanceof Date)
  766. newval = Upfront.Util.format_date(val, true, true).replace(/\//g, '-');
  767. Backbone.Model.prototype.set.call(this, key, newval, options);
  768. }
  769. return this;
  770. }
  771. }),
  772. /**
  773. * Represent a collection of WPModels, to fetch and save Posts, Comments or metadata list.
  774. * It handle pagination, sorting and hierarchical lists out of the box.
  775. */
  776. WPCollection = Backbone.Collection.extend({
  777. //WP ajax action
  778. action: 'upfront-wp-model',
  779. // Collection's attributes to be sent with their values in every fetch request.
  780. fetchAttributes: [],
  781. // Collection's attributes to be sent with their values in every save request.
  782. saveAttributes: [],
  783. // These are used to know what has changed since the last save.
  784. changedModels: [],
  785. addedModels: [],
  786. removedModels: [],
  787. isNew: true,
  788. // Model attribute where the parent id is stored.
  789. parentAttribute: false,
  790. // Attribute to store the collection of children.
  791. childrenAttribute: false,
  792. // Used to store the store the models without parents. (Only when parentAttribute and childrenAttribute are set)
  793. orphans: {},
  794. // Attribute to sort the collection. Use the reSort methods to change it properly.
  795. orderby: false,
  796. order: 'desc',
  797. // Pagination default parameters. Set pageSize to -1 to deactivate pagination.
  798. pagination: {
  799. pages: 1,
  800. pageSize: 10,
  801. currentPage:1,
  802. totalElements: 0,
  803. loaded: {}
  804. },
  805. // Used to keep the last fetch options and be able to fetch more pages.
  806. lastFetchOptions: {},
  807. /**
  808. * Loads the Collection with models from the database. Uses the collectionName attribute to know what to fetch.
  809. * @param {Object} data Extra data to be sent with the fetch request
  810. * @return {jQuery.Deferred} Promise for the fetch request. The server response will be passed as argument to the done function.
  811. */
  812. fetch: function(data) {
  813. var me = this,
  814. postdata = {
  815. action: 'fetch_' + this.collectionName,
  816. id: this.id
  817. }
  818. ;
  819. if(this.orderby){
  820. postdata['orderby'] = this.orderby;
  821. postdata['order'] = this.order;
  822. }
  823. _.each(this.fetchAttributes, function(attr){
  824. postdata[attr] = me[attr];
  825. });
  826. postdata = this.checkPostFlush(_.extend(postdata, data));
  827. // Set change observers here, so we leave the initialize method
  828. // easily overridable.
  829. this.changedModels = [];
  830. this.addedModels = [];
  831. this.removedModels = [];
  832. this.off('change', this.updateChanged, this);
  833. this.on('change', this.updateChanged, this);
  834. this.off('add', this.updateAdded, this);
  835. this.on('add', this.updateAdded, this);
  836. this.off('remove', this.updateRemoved, this);
  837. this.on('remove', this.updateRemoved, this);
  838. this.isNew = false;
  839. return this.post(postdata)
  840. .done(function(response){
  841. if(response.data.pagination){
  842. var pagination = response.data.pagination,
  843. models = [];
  844. if(postdata.flush || me.pagination.totalElements != pagination.total){
  845. me.pagination = {
  846. totalElements: pagination.total,
  847. pageSize: pagination.page_size,
  848. // If pages total is given use that. Otherwise calculate it.
  849. // This is because hierarchical child pages are placed on parent page despite page limit. Thus it is calculated elsewhere if hierarchical.
  850. pages: (pagination.pages ? Math.ceil(pagination.pages) : Math.ceil(pagination.total / pagination.page_size)),
  851. currentPage: pagination.page,
  852. loaded: postdata.flush ? {} : me.pagination.loaded
  853. };
  854. me.pagination.loaded[pagination.page] = true;
  855. _.each(response.data.results, function(modelData){
  856. var model = new me.model(modelData);
  857. model.belongsToPage = pagination.page;
  858. models.push(model);
  859. });
  860. me.reset(models);
  861. }
  862. else {
  863. me.pagination.currentPage = pagination.page;
  864. me.pagination.loaded[pagination.page] = true;
  865. _.each(response.data.results, function(modelData){
  866. var model = new me.model(modelData);
  867. model.belongsToPage = pagination.page;
  868. me.add(model, {silent: true, merge: true});
  869. });
  870. me.trigger('reset', me);
  871. }
  872. } else
  873. me.reset(response.data.results);
  874. if (response.data.filtering) {
  875. // Filtering dropdown data for post list.
  876. me.filtering = response.data.filtering;
  877. }
  878. }
  879. );
  880. },
  881. /**
  882. * Send a POST request to the server setting all the parameters needed to communicate with
  883. * the models endpoint.
  884. * @param {Object} data Data to be sent with the request.
  885. * @return {jQuery.Deferred} A promise for the response. The server response will be passed as argument to the done function.
  886. */
  887. post: function(data){
  888. data = _.isObject(data) ? _.clone(data) : {};
  889. data.model_action = data.action;
  890. data.action = this.action;
  891. return Upfront.Util.post(data);
  892. },
  893. /**
  894. * Store the data base. Different lists are sent to the server with all, added, changed and removed models
  895. * for making easy to store the changes.
  896. * @return {jQuery.Deferred} A promise for the response. The server response will be passed as argument to the done function.
  897. */
  898. save: function(){
  899. var me = this,
  900. data = {
  901. all: this.toJSON(),
  902. added: [],
  903. changed: [],
  904. removed: [],
  905. action: 'save_' + this.collectionName
  906. }
  907. ;
  908. if(this.isNew){
  909. data.added = data.all;
  910. }
  911. else{
  912. this.each(function(model){
  913. if(me.addedModels.indexOf(model.id) != -1)
  914. data.added.push(model.toJSON());
  915. if(me.changedModels.indexOf(model.id) != -1)
  916. data.changed.push(model.toJSON());
  917. if(me.removedModels.indexOf(model.id) != -1)
  918. data.removed.push(model.toJSON());
  919. });
  920. }
  921. this.isNew = false;
  922. _.each(this.saveAttributes, function(attr){
  923. data[attr] = me[attr];
  924. });
  925. return this.post(data)
  926. .done(function(response){
  927. me.reset(response.data);
  928. me.addedModels = [];
  929. me.changedModels = [];
  930. me.removedModels = [];
  931. });
  932. },
  933. /**
  934. * Used to synchro the changed model list.
  935. * @param {WPModel} model The model to add to the changed list.
  936. * @return {null}
  937. */
  938. updateChanged: function(model){
  939. var id = model.id,
  940. addedIndex = _.indexOf(this.addedModels, id)
  941. ;
  942. if(addedIndex != -1)
  943. this.addedModels[addedIndex] = id;
  944. else if(!_.contains(this.changedModels, id))
  945. this.changedModels.push(id);
  946. },
  947. /**
  948. * Used to synchro the added model list.
  949. * @param {WPModel} model The model to add to the added list.
  950. * @return {null}
  951. */
  952. updateAdded: function(model){
  953. var id = model.id,
  954. removedIndex = _.indexOf(this.removedModels, id)
  955. ;
  956. if(removedIndex != -1){
  957. this.removedModels.splice(removedIndex, 1);
  958. this.changedModels.push(id);
  959. }
  960. else if(!_.contains(this.addedModels, id))
  961. this.addedModels.push(id);
  962. },
  963. /**
  964. * Used to synchro the removed model list.
  965. * @param {WPModel} model The model to add to the removed list.
  966. * @return {null}
  967. */
  968. updateRemoved: function(model){
  969. var id = model.id,
  970. addedIndex = _.indexOf(this.addedModels, id),
  971. changedIndex = _.indexOf(this.changedModels, id)
  972. ;
  973. if(addedIndex != -1)
  974. this.addedModels.splice(addedIndex, 1);
  975. else if(!_.contains(this.removedModels, id))
  976. this.removedModels.push(id);
  977. if(changedIndex != -1)
  978. this.changedModels.splice(changedIndex, 1);
  979. },
  980. /**
  981. * Override the Backbone.Collection function to handle hierarchical data when the
  982. * parentAttribute and childrenAttribute are set for the collection.
  983. * @param {Array} models Array of data for the new models
  984. * @param {Object} options Options for the adding. See http://backbonejs.org/#Collection-add
  985. */
  986. add: function(models, options){
  987. //Check for hierarchy
  988. if(!this.parentAttribute || !this.childrenAttribute)
  989. return Backbone.Collection.prototype.add.call(this, models, options);
  990. var me = this;
  991. if(_.isArray(models)){
  992. _.each(models, function(model){
  993. me.add(model, options);
  994. });
  995. }
  996. else {
  997. var parent_id = 0,
  998. model = models;
  999. if(this.model && model instanceof this.model){
  1000. parent_id = model.get(this.parentAttribute);
  1001. }
  1002. else if(_.isObject(model)){
  1003. parent_id = model[this.parentAttribute];
  1004. if(this.model)
  1005. model = new this.model(models);
  1006. }
  1007. else
  1008. return Backbone.Collection.prototype.add.call(this, model, options);
  1009. // We got the model to add
  1010. // Add the children
  1011. if(this.orphans[model.id]){
  1012. if(!model[this.childrenAttribute])
  1013. model[this.childrenAttribute] = new this.constructor(this.orphans[model.id], options);
  1014. else
  1015. model[this.childrenAttribute].add(this.orphans[model.id], options);
  1016. delete this.orphans[model.id];
  1017. }
  1018. else if(!model[this.childrenAttribute])
  1019. model[this.childrenAttribute] = new this.constructor([], options);
  1020. if(parent_id){
  1021. // Is it a child?
  1022. var parent = this.get(parent_id);
  1023. if(parent){
  1024. parent[this.childrenAttribute].add(model, options);
  1025. }
  1026. else{ //An orphan
  1027. var parentOrphans = this.orphans[parent_id];
  1028. if(parentOrphans)
  1029. parentOrphans.push(model);
  1030. else
  1031. this.orphans[parent_id] = [model];
  1032. }
  1033. }
  1034. // Add the model to the collection
  1035. return Backbone.Collection.prototype.add.call(this, model, options);
  1036. }
  1037. },
  1038. /**
  1039. * Overrides Backbone.Collection.remove to handle hierarchical data when the
  1040. * parentAttribute and childrenAttribute are set for the collection.
  1041. *
  1042. * @param {Array|Model} models Models to remove.
  1043. * @param {Object} options Options for removing elements. See http://backbonejs.org/#Collection-remove
  1044. * @return {WPCollection} this
  1045. */
  1046. remove: function(models, options){
  1047. //Check for hierarchy
  1048. if(!this.parentAttribute || !this.childrenAttribute)
  1049. return Backbone.Collection.prototype.remove.call(this, models, options);
  1050. var me = this;
  1051. models = _.isArray(models) ? models.slice() : [models];
  1052. // Delete the children
  1053. for (i = 0, l = models.length; i < l; i++) {
  1054. var model = this.get(models[i]);
  1055. if(model){
  1056. model[this.childrenAttribute].each(function(child){
  1057. me.remove(child, options);
  1058. });
  1059. }
  1060. }
  1061. return Backbone.Collection.prototype.remove.call(this, models, options);
  1062. },
  1063. /**
  1064. * Get a page from the collection using the pagination parameters. The model must be fetched before
  1065. * using this function. A page is known to be loaded checking the WPCollection.pagination.loaded[pageNumber]
  1066. * attribute.
  1067. *
  1068. * @param {Number} pageNumber The page number
  1069. * @return {Array} Models that belongs to the requested paged
  1070. */
  1071. getPage: function(pageNumber){
  1072. var me = this;
  1073. return this.filter(function(model){
  1074. return model.belongsToPage == pageNumber;
  1075. });
  1076. },
  1077. /**
  1078. * Load the models of the given page from the database.
  1079. * @param {Number} pageNumber The number of the page to fetch.
  1080. * @return {jQuery.Deferred} A promise for the fetching. The server response will be passed as arguments for the done function.
  1081. */
  1082. fetchPage: function(pageNumber, options){
  1083. if(!options)
  1084. options = {};
  1085. if(!options.flush && this.pagination.loaded[pageNumber]){
  1086. this.pagination.currentPage = pageNumber;
  1087. var models = this.getPage(pageNumber),
  1088. results = [];
  1089. _.each(models, function(model){
  1090. results.push(model.toJSON());
  1091. });
  1092. this.pagination.currentPage = pageNumber;
  1093. return jQuery.Deferred().resolve({results: results});
  1094. }
  1095. return this.fetch(_.extend({page: pageNumber, limit: this.pagination.pageSize}, options));
  1096. },
  1097. /**
  1098. * Re-Sort the collection based on the model attribute. This always flush the collection elements.
  1099. * @param {String} attribute Model attribute for using to sort.
  1100. * @param {String} asc asc|desc Order of the sorting.
  1101. * @return {[type]} [description]
  1102. */
  1103. reSort: function(attribute, asc){
  1104. var direction = asc == 'asc' ? 'asc' : 'desc';
  1105. this.orderby = attribute;
  1106. this.order = direction;
  1107. return this.fetch({page: 0, sort: attribute, direction: direction});
  1108. },
  1109. /**
  1110. * Check if the fetch options must be flushed.
  1111. */
  1112. checkPostFlush: function(fetchOptions){
  1113. var me = this,
  1114. flush = false,
  1115. newOptions = _.clone(fetchOptions)
  1116. ;
  1117. if(fetchOptions.flush){
  1118. delete newOptions.flush;
  1119. this.lastFetchOptions = newOptions;
  1120. return fetchOptions;
  1121. }
  1122. _.each(fetchOptions, function(value, key){
  1123. if(['limit', 'page'].indexOf(key) == -1)
  1124. flush = flush || me.lastFetchOptions[key] != value;
  1125. });
  1126. if(flush){
  1127. me.lastFetchOptions = _.clone(fetchOptions);
  1128. newOptions.flush = true;
  1129. }
  1130. else
  1131. newOptions = _.extend(this.lastFetchOptions, newOptions);
  1132. return newOptions;
  1133. }
  1134. }),
  1135. Post = WPModel.extend({
  1136. modelName: 'post',
  1137. defaults: {
  1138. ID: 0,
  1139. post_author: 0,
  1140. post_date: new Date(),
  1141. post_date_gmt: new Date(),
  1142. post_content: '',
  1143. post_title: '',
  1144. post_excerpt: '',
  1145. post_status: '',
  1146. comment_status: '',
  1147. ping_status: '',
  1148. post_password: '',
  1149. post_name: '',
  1150. to_ping: [], // To do initialize
  1151. pinged: [], // To do initialize
  1152. post_modified: new Date(),
  1153. post_modified_gmt: new Date(),
  1154. post_content_filtered: '',
  1155. post_parent: 0,
  1156. guid: '',
  1157. menu_order: 0,
  1158. post_type: 'post',
  1159. post_mime_type: '',
  1160. comment_count: 0,
  1161. permalink: ''
  1162. },
  1163. saveAttributes: ['sticky'],
  1164. author: false,
  1165. comments: false,
  1166. parent: false,
  1167. terms: false,
  1168. meta: false,
  1169. initialize: function(model, options){
  1170. var me = this;
  1171. if(model){
  1172. if(model['id'])
  1173. this.set(this.idAttribute, model['id']);
  1174. if(model['author'])
  1175. this.author = new Upfront.Models.User(model['author']);
  1176. if(model['meta'])
  1177. this.meta = new Upfront.Collections.MetaList(model['meta'], {objectId: this.id, metaType: 'post'});
  1178. }
  1179. },
  1180. getVisibility: function(){
  1181. if(this.get('post_status') == 'private')
  1182. return 'private';
  1183. if(this.get('post_password'))
  1184. return 'password';
  1185. if(this.sticky)
  1186. return 'sticky';
  1187. return 'public';
  1188. },
  1189. setVisibility: function(visibility){
  1190. this.sticky = 0;
  1191. if(visibility == 'password'){
  1192. this.set('post_status', 'publish');
  1193. } else {
  1194. this.set('post_password', '');
  1195. if(visibility == 'private')
  1196. this.set('post_status', 'private');
  1197. else if(visibility == 'sticky')
  1198. this.sticky = 1;
  1199. else if(visibility == 'public')
  1200. this.set('post_status', 'publish');
  1201. }
  1202. },
  1203. fetch: function(data) {
  1204. var me = this;
  1205. return WPModel.prototype.fetch.call(this, data)
  1206. .done(function(response){
  1207. if(response.data.author)
  1208. me.author = new Upfront.Models.User(response.data.author);
  1209. if(response.data.meta)
  1210. me.meta = new Upfront.Collections.MetaList(response.data.meta, {objectId: me.id, metaType: 'post'});
  1211. me.sticky = response.data.sticky;
  1212. })
  1213. ;
  1214. },
  1215. idAttribute: 'ID',
  1216. fetch_comments: function(data){
  1217. var me = this;
  1218. data = _.isObject(data) ? data : {};
  1219. this.comments = new Upfront.Collections.CommentList([], {postId: this.id});
  1220. return this.comments.fetch();
  1221. },
  1222. fetch_parent: function() {
  1223. if(!this.get('post_parent'))
  1224. return false;
  1225. var me = this,
  1226. data = {
  1227. action: 'fetch_post',
  1228. id: this.get('post_parent')
  1229. }
  1230. ;
  1231. return this.post(data)
  1232. .done(function(response){
  1233. me.parent = new Post(response.data);
  1234. });
  1235. },
  1236. fetch_author: function() {
  1237. console.log('Fetch author not yet implemented.');
  1238. },
  1239. fetch_terms: function(type){
  1240. if(!type)
  1241. return false;
  1242. return this.post({
  1243. taxonomy: type,
  1244. post_id: this.get('ID'),
  1245. action: 'get_terms'
  1246. });
  1247. },
  1248. fetch_meta: function(){
  1249. this.meta = new Upfront.Collection.MetaList([], {objectId: this.id, metaType: 'post'});
  1250. return this.meta.fetch();
  1251. }
  1252. }),
  1253. PageTemplate = WPModel.extend({
  1254. modelName: 'template',
  1255. defaults: {
  1256. },
  1257. initialize: function(model, options){
  1258. var me = this;
  1259. }
  1260. }),
  1261. PostList = WPCollection.extend({
  1262. collectionName: 'post_list',
  1263. model: Post,
  1264. parentAttribute: 'post_parent',
  1265. childrenAttribute: 'children',
  1266. postId: false,
  1267. postType: 'post',
  1268. withMeta: false,
  1269. withAuthor: false,
  1270. withThumbnail: false,
  1271. fetchAttributes: ['postId', 'postType', 'withMeta', 'withAuthor', 'withThumbnail'],
  1272. initialize: function(models, options){
  1273. if(options){
  1274. if(options.postId)
  1275. this.postId = options.postId;
  1276. if(options.postType)
  1277. this.postType = options.postType;
  1278. if(options.withMeta)
  1279. this.withMeta = options.withMeta;
  1280. if(options.withAuthor)
  1281. this.withAuthor = options.withAuthor;
  1282. if(options.withThumbnail)
  1283. this.withThumbnail = options.withThumbnail;
  1284. }
  1285. }
  1286. });
  1287. PageTemplateList = WPCollection.extend({
  1288. collectionName: 'page_templates',
  1289. model: PageTemplate,
  1290. postId: false,
  1291. templateObject: false,
  1292. fetchAttributes: ['postId'],
  1293. initialize: function(models, options){
  1294. if(options){
  1295. if(options.postId)
  1296. this.postId = options.postId;
  1297. }
  1298. },
  1299. fetch: function(options){
  1300. var me = this;
  1301. return WPCollection.prototype.fetch.call(this, options)
  1302. .done(function(response){
  1303. me.templateObject = response.results;
  1304. })
  1305. ;
  1306. }
  1307. });
  1308. var Comment = WPModel.extend({
  1309. modelName: 'comment',
  1310. defaults: {
  1311. comment_ID: 0,
  1312. comment_post_id: 0,
  1313. comment_author: '',
  1314. comment_author_email: '',
  1315. comment_author_url: '',
  1316. comment_author_IP: '0.0.0.0',
  1317. comment_date: new Date(),
  1318. comment_date_gmt: new Date(),
  1319. comment_content: '',
  1320. comment_karma: '',
  1321. comment_agent: '',
  1322. comment_type: '',
  1323. comment_approved: '0',
  1324. comment_parent: 0,
  1325. user_id: 0
  1326. },
  1327. idAttribute: 'comment_ID',
  1328. initialize: function(options){
  1329. if(options && options['id'])
  1330. this.set(this.idAttribute, options['id']);
  1331. },
  1332. trash: function(trashed){
  1333. if(trashed)
  1334. this.set('comment_approved', 'trash');
  1335. else if(!trashed && this.get('comment_approved') == 'trash')
  1336. this.set('comment_approved', '0');
  1337. return this;
  1338. },
  1339. spam: function(spammed){
  1340. if(spammed)
  1341. this.set('comment_approved', 'spam');
  1342. else if(!spammed && this.get('comment_approved') == 'spam')
  1343. this.set('comment_approved', '0');
  1344. return this;
  1345. },
  1346. approve: function(approved){
  1347. if(approved)
  1348. this.set('comment_approved', '1');
  1349. else if(!approved && this.get('comment_approved') == '1')
  1350. this.set('comment_approved', '0');
  1351. return this;
  1352. },
  1353. isTrash: function(){
  1354. return this.get('comment_approved') == 'trash';
  1355. },
  1356. isApproved: function(){
  1357. return this.get('comment_approved') == '1';
  1358. },
  1359. isSpam: function(){
  1360. return this.get('comment_approved') == 'spam';
  1361. }
  1362. }),
  1363. CommentList = WPCollection.extend({
  1364. model: Comment,
  1365. collectionName: 'comment_list',
  1366. postId: false,
  1367. fetchAttributes: ['postId', 'commentType'],
  1368. parentAttribute: 'comment_parent',
  1369. childrenAttribute: 'replies',
  1370. commentType: 'comment', // all, comment, trackback, pingback
  1371. initialize: function(models, options){
  1372. if(options.postId)
  1373. this.postId = options.postId;
  1374. },
  1375. save: function(){
  1376. console.error('CommentList save: Use single comment save instead.');
  1377. }
  1378. }),
  1379. Meta = Backbone.Model.extend({
  1380. defaults: {
  1381. meta_key: '',
  1382. meta_value: ''
  1383. },
  1384. idAttribute: 'meta_key'
  1385. }),
  1386. MetaList = WPCollection.extend({
  1387. model: Meta,
  1388. collectionName: 'meta_list',
  1389. metaType: 'post',
  1390. objectId: 0,
  1391. fetchAttributes: ['objectId', 'metaType'],
  1392. saveAttributes: ['objectId', 'metaType'],
  1393. initialize: function(models, options){
  1394. var metaModels = [];
  1395. if(options.objectId)
  1396. this.objectId = options.objectId;
  1397. if(options.metaType)
  1398. this.metaType = options.metaType;
  1399. },
  1400. getValue: function(key){
  1401. var meta = this.get(key);
  1402. if(!meta)
  1403. return undefined;
  1404. return meta.get('meta_value');
  1405. },
  1406. setValue: function(key, value){
  1407. var meta = this.get(key);
  1408. if(!meta){
  1409. meta = new Meta({meta_key: key, meta_value: value});
  1410. this.add(meta);
  1411. }
  1412. else{
  1413. meta.set('meta_value', value);
  1414. this.changedModels.push(meta.id);
  1415. }
  1416. return meta;
  1417. }
  1418. }),
  1419. Term = WPModel.extend({
  1420. modelName: 'term',
  1421. defaults: {
  1422. term_id: 0,
  1423. name: '',
  1424. slug: '',
  1425. term_group: '',
  1426. term_taxonomy_id: 0,
  1427. taxonomy: '',
  1428. description: '',
  1429. parent: '',
  1430. count: 0
  1431. },
  1432. idAttribute: 'term_id',
  1433. taxonomy: false,
  1434. fetchAttributes: ['taxonomy'],
  1435. saveAttributes: ['taxonomy'],
  1436. initialize: function(model, options){
  1437. if(model && model.taxonomy)
  1438. this.taxonomy = model.taxonomy;
  1439. },
  1440. save: function(data){
  1441. var me = this;
  1442. return WPModel.prototype.save.call(this, data).
  1443. done(function(response){
  1444. me.set('term_id', response.data.term_id);
  1445. })
  1446. ;
  1447. }
  1448. }),
  1449. TermList = WPCollection.extend({
  1450. collectionName: 'term_list',
  1451. model: Term,
  1452. taxonomy: false,
  1453. taxonomyObject: false,
  1454. postId: false,
  1455. fetchAttributes: ['taxonomy', 'postId'],
  1456. saveAttributes: ['taxonomy', 'postId'],
  1457. parentAttribute: 'parent',
  1458. childrenAttribute: 'children',
  1459. initialize: function(models, options){
  1460. if(options){
  1461. if(options.taxonomy){
  1462. this.taxonomy = options.taxonomy;
  1463. }
  1464. if(options.postId)
  1465. this.postId = options.postId;
  1466. }
  1467. },
  1468. fetch: function(options){
  1469. var me = this;
  1470. return WPCollection.prototype.fetch.call(this, options)
  1471. .done(function(response){
  1472. me.taxonomyObject = response.dat

Large files files are truncated, but you can click here to view the full file