Merge branch 'master' into view-refactor
[redakcja.git] / project / static / js / app.js
1 /*global Class*/
2 var editor;
3 var panel_hooks;
4
5
6 (function(){
7   // Classes
8   var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
9   this.Class = function(){};
10   Class.extend = function(prop) {
11     var _super = this.prototype;
12     initializing = true;
13     var prototype = new this();
14     initializing = false;
15     for (var name in prop) {
16       prototype[name] = typeof prop[name] == "function" &&
17         typeof _super[name] == "function" && fnTest.test(prop[name]) ?
18         (function(name, fn){
19           return function() {
20             var tmp = this._super;
21             this._super = _super[name];
22             var ret = fn.apply(this, arguments);       
23             this._super = tmp;           
24             return ret;
25           };
26         })(name, prop[name]) :
27         prop[name];
28     }   
29     function Class() {
30       if ( !initializing && this.init )
31         this.init.apply(this, arguments);
32     }
33     Class.prototype = prototype;
34     Class.constructor = Class;
35     Class.extend = arguments.callee;   
36     return Class;
37   };
38   
39   // Templates
40   var cache = {};
41
42   this.render_template = function render_template(str, data){
43     // Figure out if we're getting a template, or if we need to
44     // load the template - and be sure to cache the result.
45     var fn = !/^[\d\s-_]/.test(str) ?
46       cache[str] = cache[str] ||
47         render_template(document.getElementById(str).innerHTML) :
48
49       // Generate a reusable function that will serve as a template
50       // generator (and which will be cached).
51       new Function("obj",
52         "var p=[],print=function(){p.push.apply(p,arguments);};" +
53
54         // Introduce the data as local variables using with(){}
55         "with(obj){p.push('" +
56
57         // Convert the template into pure JavaScript
58         str
59           .replace(/[\r\t\n]/g, " ")
60           .split("<%").join("\t")
61           .replace(/((^|%>)[^\t]*)'/g, "$1\r")
62           .replace(/\t=(.*?)%>/g, "',$1,'")
63           .split("\t").join("');")
64           .split("%>").join("p.push('")
65           .split("\r").join("\\'")
66       + "');}return p.join('');");
67
68       // Provide some basic currying to the user
69     return data ? fn( data ) : fn;
70   };
71 })();
72
73 (function() {
74   var slice = Array.prototype.slice;
75   
76   function update(array, args) {
77     var arrayLength = array.length, length = args.length;
78     while (length--) array[arrayLength + length] = args[length];
79     return array;
80   };
81   
82   function merge(array, args) {
83     array = slice.call(array, 0);
84     return update(array, args);
85   };
86   
87   Function.prototype.bind = function(context) {
88     if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
89       return this;
90     } 
91     var __method = this;
92     var args = slice.call(arguments, 1);
93     return function() {
94       var a = merge(args, arguments);
95       return __method.apply(context, a);
96     }
97   }
98   
99 })();
100  
101 var panels = [];
102
103 var documentsUrl = '/api/documents/';
104
105
106 var Model = Class.extend({
107   observers: {},
108   
109   init: function() {},
110   
111   signal: function(event, data) {
112     console.log('signal', this, event, data);
113     if (this.observers[event]) {
114       for (key in this.observers[event]) {
115         this.observers[event][key](event, data);
116       }
117     };
118     return this;
119   },
120   
121   addObserver: function(observer, event, callback) {
122     if (!this.observers[event]) {
123       this.observers[event] = {};
124     }
125     this.observers[event][observer.id] = callback;
126     return this;
127   },
128   
129   removeObserver: function(observer, event) {
130     if (!event) {
131       for (e in this.observers) {
132         this.removeObserver(observer, e);
133       }
134     } else {
135       delete this.observers[event][observer.id];
136     }
137     return this;
138   }
139 });
140
141
142 var XMLModel = Model.extend({
143   parent: null,
144   data: '',
145   serverURL: null,
146   needsReload: false,
147   
148   init: function(parent, serverURL) {
149     this.parent = parent;
150     this.serverURL = serverURL;
151   },
152   
153   getData: function() {
154     if (!this.data) {
155       this.reload();
156     }
157     return this.data;
158   },
159   
160   setData: function(data) {
161     this.data = data;
162     this.dataChanged();
163   },
164   
165   reload: function() {
166     $.ajax({
167       url: this.serverURL,
168       dataType: 'text',
169       success: this.reloadSucceeded.bind(this)
170     });
171   },
172   
173   reloadSucceeded: function(data) {
174     this.data = data;
175     this.signal('reloaded');
176   },
177   
178   dataChanged: function() {
179     this.parent.modelChanged('xml');
180     this.signal('dataChanged');
181   },
182   
183   needsReload: function() {
184     this.needsReload = true;
185     this.signal('needsReload');
186   }
187 })
188
189
190
191 var HTMLModel = Model.extend({
192   parent: null,
193   data: '',
194   serverURL: null,
195   needsReload: false,
196   
197   init: function(parent, serverURL) {
198     this.parent = parent;
199     this.serverURL = serverURL;
200   },
201   
202   getData: function() {
203     if (!this.data) {
204       this.reload();
205     }
206     return this.data;
207   },
208   
209   setData: function(data) {
210     console.log('setData');
211     if (this.data != data) {
212       this.data = data;
213       this.dataChanged();
214     }
215   },
216   
217   reload: function() {
218     $.ajax({
219       url: this.serverURL,
220       dataType: 'text',
221       success: this.reloadSucceeded.bind(this)
222     });
223   },
224   
225   reloadSucceeded: function(data) {
226     this.data = data;
227     this.signal('reloaded');
228   },
229   
230   dataChanged: function() {
231     this.parent.modelChanged('html');
232   },
233   
234   needsReload: function() {
235     this.needsReload = true;
236     this.signal('needsReload');
237   }
238 })
239
240
241 var DocumentModel = Model.extend({
242   data: null, // name, text_url, latest_rev, latest_shared_rev, parts_url, dc_url, size
243   xml: null,
244   html: null,
245   contentModels: {},
246   
247   init: function() {
248     this.getData();
249   },
250   
251   getData: function() {
252     console.log('DocumentModel#getData');
253     $.ajax({
254       cache: false,
255       url: documentsUrl + fileId,
256       dataType: 'json',
257       success: this.successfulGetData.bind(this)
258     });
259   },
260   
261   successfulGetData: function(data) {
262     console.log('DocumentModel#successfulGetData:', data);
263     this.data = data;
264     this.contentModels = {
265       'xml': new XMLModel(this, data.text_url)
266     };
267   },
268   
269   modelChanged: function(contentModelName) {
270     for (modelName in this.contentModels) {
271       if (!(modelName == contentModelName)) {
272         this.contentModels[modelName].needsReload();
273       }
274     }
275   }
276 });
277
278 var leftPanelView, rightPanelContainer, doc;
279
280 $(function() {
281   doc = new DocumentModel();
282   var splitView = new SplitView('#splitview', doc);
283   leftPanelView = new PanelContainerView('#left-panel-container', doc);
284   rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
285 });