Fix file permissions: if it is not a script, it should not be executable.
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / lib / greybox / GreyBox_v5_5 / compression_lib / AJS_minify.py
1 #!/usr/bin/env python
2 #Last-update: 08/05/07 12:39:17
3 import re
4 import sys
5 from sets import Set
6
7 ##
8 # External files
9 #
10 AJS_SRC = 'AJS.js'
11 AJS_MINI_SRC = 'AJS_compressed.js'
12
13
14 ##
15 # Standard stuff that may change in the future
16 #
17 DOM_SHORTCUTS = [
18     "ul", "li", "td", "tr", "th",
19     "tbody", "table", "input", "span", "b",
20     "a", "div", "img", "button", "h1",
21     "h2", "h3", "br", "textarea", "form",
22     "p", "select", "option", "iframe", "script",
23     "center", "dl", "dt", "dd", "small",
24     "pre", "tn"
25 ]
26
27 FN_SHORTCUTS = {
28     '$': 'getElement',
29     '$$': 'getElements',
30     '$f': 'getFormElement',
31     '$b': 'bind',
32     '$p': 'partial',
33     '$A': 'createArray',
34     'DI': 'documentInsert',
35     'ACN': 'appendChildNodes',
36     'RCN': 'replaceChildNodes',
37     'AEV': 'addEventListener',
38     'REV': 'removeEventListener',
39     '$bytc': 'getElementsByTagAndClassName'
40 }
41
42 AJS_TEMPLATE = """//AJS JavaScript library (minify'ed version)
43 //Copyright (c) 2006 Amir Salihefendic. All rights reserved.
44 //Copyright (c) 2005 Bob Ippolito. All rights reserved.
45 //License: http://www.opensource.org/licenses/mit-license.php
46 //Visit http://orangoo.com/AmiNation/AJS for full version.
47 AJS = {
48 BASE_URL: "",
49 drag_obj: null,
50 drag_elm: null,
51 _drop_zones: [],
52 _cur_pos: null,
53
54 %(functions)s
55 }
56
57 AJS.$ = AJS.getElement;
58 AJS.$$ = AJS.getElements;
59 AJS.$f = AJS.getFormElement;
60 AJS.$p = AJS.partial;
61 AJS.$b = AJS.bind;
62 AJS.$A = AJS.createArray;
63 AJS.DI = AJS.documentInsert;
64 AJS.ACN = AJS.appendChildNodes;
65 AJS.RCN = AJS.replaceChildNodes;
66 AJS.AEV = AJS.addEventListener;
67 AJS.REV = AJS.removeEventListener;
68 AJS.$bytc = AJS.getElementsByTagAndClassName;
69
70 AJS.addEventListener(window, 'unload', AJS._unloadListeners);
71 AJS._createDomShortcuts();
72
73 %(AJSClass)s
74
75 %(AJSDeferred)s
76 script_loaded = true;
77 """
78
79
80 def getAjsCode():
81     return open(AJS_SRC).read()
82
83 def writeAjsMini(code):
84     open(AJS_MINI_SRC, "w").write(code)
85
86
87 class AjsAnalyzer:
88
89     def __init__(self):
90         self.code = getAjsCode()
91         self.ajs_fns = {}
92         self.ajs_deps = {}
93         self._parseAJS()
94         self._findDeps()
95
96     def _parseAJS(self):
97         ajs_code = re.search("AJS =(.|\n)*\n}\n", self.code).group(0)
98         fns = re.findall("\s+((\w*?):.*?{(.|\n)*?\n\s*})(,|\n+})\n", ajs_code)
99         for f in fns:
100             self.ajs_fns[f[1]] = f[0]
101
102     def getFnCode(self, fn_name, caller=None):
103         """
104         Returns the code of function and it's dependencies as a list
105         """
106         fn_name = self._unfoldFn(fn_name)
107         r = []
108         if self.ajs_fns.get(fn_name):
109             r.append(self.ajs_fns[fn_name])
110             for dep_fn in self.ajs_deps[fn_name]:
111                 if fn_name != dep_fn and dep_fn != caller:
112                     r.extend(self.getFnCode(dep_fn, fn_name))
113         elif fn_name not in ['listeners', 'Class']:
114             print 'Could not find "%s"' % fn_name
115         return r
116
117     def getAjsClassCode(self):
118         return re.search("AJS.Class =(.|\n)*\n};\n", self.code).group(0)
119
120     def getAjsDeferredCode(self):
121         return re.search("AJSDeferred =(.|\n)*\n};\n", self.code).group(0)
122
123     def _findDeps(self):
124         """
125         Parses AJS and for every function it finds dependencies for the other functions.
126         """
127         for fn_name, fn_code in self.ajs_fns.items():
128             self.ajs_deps[fn_name] = self._findFns(fn_code)
129
130     def _findFns(self, inner):
131         """
132         Searches after AJS.fnX( in inner and returns all the fnX in a Set.
133         """
134         s = re.findall("AJS\.([\w_$]*?)(?:\(|,|\.)", inner)
135         s = list(Set(s))
136         return self._unfoldFns(s)
137
138     def _unfoldFns(self, list):
139         """
140         Unfolds:
141             AJS.B, AJS.H1 etc. to _createDomShortcuts
142             AJS.$ to AJS.getElement etc.
143         """
144         return [self._unfoldFn(n) for n in list]
145
146     def _unfoldFn(self, fn_name):
147         if fn_name.lower() in DOM_SHORTCUTS:
148             return "_createDomShortcuts"
149         elif FN_SHORTCUTS.get(fn_name):
150             return FN_SHORTCUTS[fn_name]
151         else:
152             return fn_name
153
154
155 class ExternalCodeAnalyzer:
156
157     def __init__(self, files):
158         self.found_ajs_fns = []
159         self.files = files
160
161     def findFunctions(self):
162         for f in self.files:
163             self.found_ajs_fns.extend( self._parseFile(f) )
164         return list(Set(self.found_ajs_fns))
165
166     def _parseFile(self, f):
167         """
168         Parses the file, looks for AJS functions and returns all the found functions.
169         """
170         code = open(f).read()
171         return re.findall("AJS\.([\w_$]*?)\(", code)
172
173
174
175 class AjsComposer:
176
177     def __init__(self, fn_list):
178         self.code = getAjsCode()
179         self.analyzer = AjsAnalyzer()
180         self.fn_list = fn_list
181
182         #Append standard functions
183         req = ['_unloadListeners', 'createDOM', '_createDomShortcuts', 'log', 'addEventListener']
184         self.fn_list.extend(req)
185
186         #Append AJSDeferred only if needed
187         in_list = lambda x: x in self.fn_list
188         if in_list('getRequest') or in_list('loadJSONDoc'):
189             self.deferred = self._minify(self.analyzer.getAjsDeferredCode())
190             self.fn_list.append('isObject')
191         else:
192             self.deferred = ''
193
194     def writeToOutput(self):
195         fns = self._getFns()
196         d = {}
197         d['functions'] = ",\n".join(fns)
198         d['AJSDeferred'] = self.deferred
199         d['AJSClass'] = self.analyzer.getAjsClassCode()
200
201         mini_code = AJS_TEMPLATE % d
202         writeAjsMini(mini_code)
203
204     def _minify(self, code):
205         new_lines = []
206         for l in code.split("\n"):
207             if l not in ['\n', '']:
208                 new_lines.append(l.lstrip())
209         return "\n".join(new_lines)
210
211     def _getFns(self):
212         """
213         Returns a list with real code of functions
214         """
215         r = []
216         for fn in self.fn_list:
217             r.extend(self.analyzer.getFnCode(fn))
218
219         r = list(Set(r))
220         return [self._minify(fn) for fn in r]
221
222
223 if __name__ == '__main__':
224     args = sys.argv
225
226     if len(args) < 3:
227         print """Usage is:
228     python AJS_minify.py [-o output_file] ajs_file js_file.js html_using_ajs.html ...
229 Example usage:
230     Using relative paths:
231         python AJS_minify.py -o AJS_mini.js AJS.js test.js index.html
232         This will create AJS_mini.js from test.js and index.html.
233     Using absolute paths:
234         python AJS_minify.py ~/Desktop/AJS/AJS.js ~/Desktop/GreyBox_v3_42/greybox/greybox.js
235         This will create a new file called '%s' that has the needed AJS functions.""" % AJS_MINI_SRC
236
237         sys.exit(0)
238
239     if sys.argv[1] == '-o':
240         AJS_MINI_SRC = sys.argv[2]
241         AJS_SRC = sys.argv[3]
242         FILES = sys.argv[4:]
243     else:
244         AJS_SRC = sys.argv[1]
245         FILES = sys.argv[2:]
246
247     print 'Parsing through:\n    %s' % "\n    ".join(FILES)
248
249     code_analyzer = ExternalCodeAnalyzer(FILES)
250     found_fns = code_analyzer.findFunctions()
251     print 'Found following AJS functions:\n    %s' % ("\n    ".join(found_fns))
252
253     composer = AjsComposer(found_fns)
254     composer.writeToOutput()
255     print "Written the minified code to '%s'" % AJS_MINI_SRC