import
[web.mtrack] / web / js.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3 include '../inc/common.php';
4
5 $age = 3600;
6
7 header('Content-Type: text/javascript');
8 header("Cache-Control: public, max-age=$age, pre-check=$age");
9 header('Expires: ' . date(DATE_COOKIE, time() + $age));
10
11 $scripts = array(
12   'excanvas.pack.js',
13   'jquery-1.4.2.min.js',
14   'jquery-ui-1.8.2.custom.min.js',
15   'jquery.asmselect.js',
16   'jquery.flot.pack.js',
17   'jquery.MultiFile.pack.js',
18   'jquery.cookie.js',
19   'jquery.treeview.js',
20   'jquery.tablesorter.js',
21   'jquery.metadata.js',
22   'jquery.markitup.js',
23   'jquery.timeago.js',
24   'json2.js',
25 );
26
27 echo "var ABSWEB = '$ABSWEB';\n";
28
29 foreach ($scripts as $name) {
30   echo "\n// $name\n";
31   readfile("js/$name");
32   echo "\n;\n";
33 }
34
35
36 $PRI_SWITCH = '';
37 foreach (MTrackDB::q('select priorityname, value from priorities')
38     ->fetchAll() as $row) {
39   $PRI_SWITCH .= "case '$row[0]': return $row[1];\n";
40 }
41 $SEV_SWITCH = '';
42 foreach (MTrackDB::q('select sevname, ordinal from severities')
43     ->fetchAll() as $row) {
44   $SEV_SWITCH .= "case '$row[0]': return $row[1];\n";
45 }
46
47 echo <<<JAVASCRIPT
48 $(document).ready(function() {
49   jQuery.timeago.settings.allowFuture = true;
50   $('abbr.timeinterval').timeago();
51   $("select[multiple]").asmSelect({
52     addItemTarget: 'bottom',
53     animate: false,
54     highlight: false,
55     removeLabel: '[x]',
56     sortable: false
57   });
58   if ($.browser.mozilla) {
59     // http://www.ryancramer.com/journal/entries/radio_buttons_firefox/
60     $("form").attr("autocomplete", "off");
61   }
62   $("textarea.wiki").markItUp({
63     nameSpace:          "wiki",
64     previewParserPath:  "{$ABSWEB}markitup-preview.php",
65     root: "{$ABSWEB}js",
66     onShiftEnter:       {keepDefault:false, replaceWith:'\\n\\n'},
67     markupSet:  [
68       {
69         name:'Heading 1', key:'1',
70         openWith:'== ', closeWith:' ==', placeHolder:'Your title here...'
71       },
72       {
73         name:'Heading 2', key:'2',
74         openWith:'=== ', closeWith:' ===', placeHolder:'Your title here...'
75       },
76       {
77         name:'Heading 3', key:'3',
78         openWith:'==== ', closeWith:' ====', placeHolder:'Your title here...'
79       },
80       {
81         name:'Heading 4', key:'4',
82         openWith:'===== ', closeWith:' =====', placeHolder:'Your title here...'
83       },
84       {
85         name:'Heading 5', key:'5',
86         openWith:'====== ', closeWith:' ======',
87         placeHolder:'Your title here...'
88       },
89       {separator:'---------------' },
90       {name:'Bold', key:'B', openWith:"'''", closeWith:"'''"},
91       {name:'Italic', key:'I', openWith:"''", closeWith:"''"},
92       {name:'Stroke through', key:'S', openWith:'~~', closeWith:'~~'},
93       {separator:'---------------' },
94       {name:'Bulleted list', openWith:' * '},
95       {name:'Numeric list', openWith:' 1. '},
96       {separator:'---------------' },
97       {name:'Quotes', openWith:'(!(> |!|>)!)'},
98       {name:'Code', openWith:'{{{\\n', closeWith:'\\n}}}'},
99       {separator:'---------------' },
100       {name:'Preview', call:'preview', className:'preview'}
101     ]
102 });
103
104   $.tablesorter.addParser({
105     id: 'ticket',
106     is: function(s) {
107       return /^#\d+/.test(s);
108     },
109     format: function(s) {
110       return $.tablesorter.formatFloat(s.replace(new RegExp(/#/g), ''));
111     },
112     type: 'numeric'
113   });
114   $.tablesorter.addParser({
115     id: 'priority',
116     is: function(s) {
117       // don't auto-detect
118       return false;
119     },
120     format: function(s) {
121       switch (s) {
122         $PRI_SWITCH
123       }
124       return s;
125     },
126     type: 'numeric'
127   });
128   $.tablesorter.addParser({
129     id: 'severity',
130     is: function(s) {
131       // don't auto-detect
132       return false;
133     },
134     format: function(s) {
135       switch (s) {
136         $SEV_SWITCH
137       }
138       return s;
139     },
140     type: 'numeric'
141   });
142   $.tablesorter.addParser({
143     id: 'mtrackdate',
144     is: function(s) {
145       // don't auto-detect
146       return false;
147     },
148     format: function(s) {
149       // relies on the textExtraction routine below to pull a
150       // date/time string out of the title portion of the abbr tag
151       return $.tablesorter.formatFloat(new Date(s).getTime());
152     },
153     type: 'numeric'
154   });
155   $("table.report, table.wiki").tablesorter({
156     textExtraction: function(node) {
157       var kid = node.childNodes[0];
158       if (kid && kid.tagName == 'ABBR') {
159         // assuming that this abbr is of class='timeinterval'
160         return kid.title;
161       }
162       // default 'simple' behavior
163       if (kid && kid.hasChildNodes()) {
164         return kid.innerHTML;
165       }
166       return node.innerHTML;
167     }
168   });
169   $('input.search[type=text]').each(function () {
170     if ($.browser.webkit) {
171       this.type = 'search';
172       $(this).attr('autosave', ABSWEB);
173       $(this).attr('results', 5);
174     } else {
175       $(this).addClass('roundsearch');
176     }
177   });
178   // Convert links that are styled after buttons into actual buttons
179   $('a.button[href]').each(function () {
180     var href = $(this).attr('href');
181     var but = $('<button type="button"/>');
182     but.text($(this).text());
183     $(this).replaceWith(but);
184     but.click(function () {
185       document.location.href = href;
186       return false;
187     });
188   });
189
190   $.fn.mtrackWatermark = function () {
191     this.each(function () {
192       var ph = $(this).attr('title');
193       if ($.browser.webkit) {
194         // Use native safari placeholder for watermark
195         $(this).attr('placeholder', ph);
196       } else {
197         // http://plugins.jquery.com/files/jquery.tinywatermark-2.0.0.js.txt
198         var w;
199         var me = $(this);
200         me.focus(function () {
201           if (w) {
202             w = 0;
203             me.removeClass('watermark').data('w', 0).val('');
204           }
205         })
206         .blur(function () {
207           if (!me.val()) {
208             w = 1;
209             me.addClass('watermark').data('w', 1).val(ph);
210           }
211         })
212         .closest('form').submit(function () {
213           if (w) {
214             me.val('');
215           }
216         });
217         me.blur();
218       }
219     });
220   };
221   // Watermarking
222   $('input[title!=""]').mtrackWatermark();
223
224   // Toggle line number display in diff visualizations, to make it easier
225   // to copy the diff contents
226   var diff_visible = true;
227   $('.togglediffcopy').click(function () {
228     diff_visible = !diff_visible;
229     if (diff_visible) {
230       $('table.code.diff tr td.lineno').show();
231       $('table.code.diff tr td.linelink').show();
232     } else {
233       $('table.code.diff tr td.lineno').hide();
234       $('table.code.diff tr td.linelink').hide();
235     }
236   });
237
238   // Syntax highlighting
239   var hl_color_scheme = 'wezterm';
240   function applyhl(name) {
241     if (hl_color_scheme != '') {
242       $('.source-code').removeClass(hl_color_scheme);
243     }
244     if (name != '') {
245       $('.source-code').addClass(name);
246     }
247     hl_color_scheme = name;
248   }
249   $('.select-hl-scheme').change(function () {
250     applyhl($(this).val());
251     var val = $(this).val();
252     $('.select-hl-scheme').each(function () {
253       $(this).val(val);
254     });
255   });
256
257   // Arrange for the footer to sink to the bottom of the window, if the window
258   // contents are not very tall
259   var last_dh = 0;
260   var last_wh = 0;
261   function mtrack_footer_position(force) {
262     var ele = $('#footer');
263     if (!force &&
264         (last_dh != $(document).height() || last_wh != $(window).height)) {
265       force = true;
266     }
267     if (force) {
268       // Force a from-scratch layout assessment; put the footer back in
269       // it's natural location in the doc
270       ele.css({
271         position: "relative",
272         "margin-top": "3em",
273         top: 0,
274       });
275     }
276     if ($(document).height() <= $(window).height()) {
277       ele.css({
278         position: "absolute",
279         "margin-top": "0",
280         top: (
281             $(window).scrollTop() +
282             $(window).height() -
283             ele.height() - 1
284           )+"px"
285       });
286     } else {
287       ele.css({
288         position: "relative",
289         "margin-top": "3em"
290       });
291     }
292     last_dh = $(document).height();
293     last_wh = $(window).height();
294   }
295   window.mtrack_footer_position = mtrack_footer_position;
296   $(window)
297     .scroll(mtrack_footer_position)
298     .resize(mtrack_footer_position);
299   function mtrack_footer_set_and_wait() {
300     mtrack_footer_position();
301     setTimeout(function () {
302       mtrack_footer_set_and_wait();
303     }, 1500);
304   }
305   mtrack_footer_set_and_wait();
306 });
307
308 JAVASCRIPT;