FIX: Zmiany wykonane w edytorze XML były ignorowane.
[redakcja.git] / platforma / static / js / app.js
1 /*global Class*/
2 var editor;
3 var panel_hooks;
4
5
6 // prevent a console.log from blowing things up if we are on a browser that
7 // does not support it
8 if (typeof console === 'undefined') {
9   window.console = {} ;
10   console.log = console.info = console.warn = console.error = function(){};
11 }
12
13
14 (function(){
15   // Classes
16   var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
17   this.Class = function(){};
18   Class.extend = function(prop) {
19     var _super = this.prototype;
20     initializing = true;
21     var prototype = new this();
22     initializing = false;
23     for (var name in prop) {
24       prototype[name] = typeof prop[name] == "function" &&
25         typeof _super[name] == "function" && fnTest.test(prop[name]) ?
26         (function(name, fn){
27           return function() {
28             var tmp = this._super;
29             this._super = _super[name];
30             var ret = fn.apply(this, arguments);       
31             this._super = tmp;           
32             return ret;
33           };
34         })(name, prop[name]) :
35         prop[name];
36     }   
37     function Class() {
38       if ( !initializing && this.init )
39         this.init.apply(this, arguments);
40     }
41     Class.prototype = prototype;
42     Class.constructor = Class;
43     Class.extend = arguments.callee;   
44     return Class;
45   };
46   
47   // Templates
48   var cache = {};
49
50   this.render_template = function render_template(str, data){
51     // Figure out if we're getting a template, or if we need to
52     // load the template - and be sure to cache the result.    
53     var fn = !/^[\d\s-_]/.test(str) ?
54       cache[str] = cache[str] ||
55         render_template(document.getElementById(str).innerHTML) :
56
57       // Generate a reusable function that will serve as a template
58       // generator (and which will be cached).
59       
60       new Function("obj",
61         "var p=[],print=function(){p.push.apply(p,arguments);};" +
62
63         // Introduce the data as local variables using with(){}
64         "with(obj){p.push('" +
65
66         // Convert the template into pure JavaScript       
67         str
68           .replace(/[\r\t\n]/g, " ")
69           .split("<%").join("\t")
70           .replace(/((^|%>)[^\t]*)'/g, "$1\r")
71           .replace(/\t=(.*?)%>/g, "',$1,'")
72           .split("\t").join("');")
73           .split("%>").join("p.push('")
74           .split("\r").join("\\'")
75       + "');}return p.join('');");
76
77       // Provide some basic currying to the user
78     return data ? fn( data ) : fn;
79   };
80 })();
81
82
83 (function() {
84   var slice = Array.prototype.slice;
85   
86   function update(array, args) {
87     var arrayLength = array.length, length = args.length;
88     while (length--) array[arrayLength + length] = args[length];
89     return array;
90   };
91   
92   function merge(array, args) {
93     array = slice.call(array, 0);
94     return update(array, args);
95   };
96   
97   Function.prototype.bind = function(context) {
98     if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
99       return this;
100     } 
101     var __method = this;
102     var args = slice.call(arguments, 1);
103     return function() {
104       var a = merge(args, arguments);
105       return __method.apply(context, a);
106     }
107   }
108   
109 })();
110
111
112 var Editor = Editor || {};
113
114 // Obiekt implementujący wzorzec KVC/KVO
115 Editor.Object = Class.extend({
116   _className: 'Editor.Object',
117   _observers: {},
118   _guid: null,
119   
120   init: function() {
121     this._observers = {};
122   },
123   
124   description: function() {
125     return this._className + '(guid = ' + this.guid() + ')';
126   },
127   
128   addObserver: function(observer, property, callback) {
129     // console.log('Add observer', observer.description(), 'to', this.description(), '[', property, ']');
130     if (!this._observers[property]) {
131       this._observers[property] = {}
132     }
133     this._observers[property][observer.guid()] = callback;
134     return this;
135   },
136   
137   removeObserver: function(observer, property) {
138     if (!property) {
139       for (var property in this._observers) {
140         this.removeObserver(observer, property)
141       }
142     } else {
143       // console.log('Remove observer', observer.description(), 'from', this.description(), '[', property, ']');
144       delete this._observers[property][observer.guid()];
145     }
146     return this;
147   },
148   
149   notifyObservers: function(property) {
150     var currentValue = this[property];
151     for (var guid in this._observers[property]) {
152       // console.log(this._observers[property][guid]);
153       // console.log('Notifying', guid, 'of', this.description(), '[', property, ']');
154       this._observers[property][guid](property, currentValue, this);
155     }
156     return this;
157   },
158   
159   guid: function() {
160     if (!this._guid) {
161       this._guid = ('editor-' + Editor.Object._lastGuid++);
162     }
163     return this._guid;
164   },
165   
166   get: function(property) {
167     return this[property];
168   },
169   
170   set: function(property, value) {
171     if (this[property] != value) {
172       this[property] = value;
173       this.notifyObservers(property);
174     }
175     return this;
176   },
177   
178   dispose: function() {
179     delete this._observers;
180   }
181 });
182
183 // Handle JSON error responses in uniform way
184 function parseXHRError(response)
185 {
186     var message = "";
187     var level = "";
188     
189     try {
190       var json = $.evalJSON(response.responseText);
191
192       if(json.reason == 'xml-parse-error') {
193           message = json.message.replace(/(line\s+)(\d+)(\s+)/i,
194             "<a class='xml-editor-ref' href='#xml-$2-1'>$1$2$3</a>");
195
196           message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i,
197             "<a class='xml-editor-ref' href='#xml-$2-$4'>$1$2$3$4</a>");
198
199           level = "warning";
200       }
201       else if(json.reason == 'xml-non-valid') {
202           message = json.message;
203           level = "warning";
204       }
205       else {
206          message = json.message || json.reason || "Nieznany błąd :((";
207          level = "error";
208       }
209     } catch(e) {
210         // not a valid JSON response
211         message = response.statusText || 'Brak połączenia z serwerem';
212         level = "error";
213     }
214     
215     return {error_message: message, error_level: level};
216 }
217
218 function parseXHRResponse(xhr) {
219     var response = {}
220     
221     if(xhr.status >= 200 && xhr.status < 300) 
222     {
223         response.success = true;
224         try {
225             response.data = $.evalJSON(xhr.responseText);
226         } catch(e) {
227             response.data = {};
228         }
229
230         return response;
231     }
232
233     return parseXHRError(xhr);
234 }
235
236 Editor.Object._lastGuid = 0;
237
238 var panels = [];