Rules can be either lists of strings to match or regular expressions.
77 |
A list consists of one or more strings separated by commas and/or new lines (CR/LF). Matching is case insensitive and terms are ORed together. Some examples:
78 |
parse, fatal, exception
79 |
parse
80 | fatal
81 | exception
82 |
parse
83 | fatal, last
84 |
85 |
Regular expressions are recognized by the format /expression/[options]. Some examples:
86 |
/(parse|fatal|exception)/
87 |
/php\s+fatal/i
88 |
/\.php\((\d+)\)$/i
89 |
90 |
For the filter rules, the default behavior is to hide log entries that match the filter rules. If the "Inverted" option is checked, then only those log entries that match the filter rules will be shown.
91 |
92 |
93 |
94 |
95 |
96 |
Elements of a log entry
97 |
98 |
99 |
1. Server
100 |
2. Timestamp
101 |
3. Log level
102 |
4. IP Address
103 |
104 |
105 |
5. Unique ID (for Splunk)
106 |
6. Namespace
107 |
7. File/Line (source of log entry)
108 |
8. User ID
109 |
110 |
111 |
9. Link to raw log entry
112 |
10. Link to callstack
113 |
11. Log message
114 |
115 |
116 |
117 |
PHP Fatal
118 |
119 |
120 |
121 |
Uncaught Exception
122 |
123 |
124 |
125 |
PHP Error
126 |
127 |
128 |
129 |
PHP Warning
130 |
131 |
132 |
133 |
PHP Info
134 |
135 |
136 |
137 |
Uncategorized
138 |
139 |
140 |
141 |
Highlighted
142 |
143 |
144 |
145 |
146 |
147 |
148 |
Raw logs and stack traces can be exported to either a gist or IRC (via irccat)
149 |
Sending log data to a gist is as simple as clicking the [GIST] link.
150 |
Exporting to IRC consists of clicking the [IRC] link and then entering the channel or nick to send the data to. To specify a channel, use the format #channel:
151 |
#push
152 | #hardware
153 | #etsy
154 |
To specify a nick, use the format @nick:
155 |
@irccat
156 | @dottie
157 |
In addition to the channel/nick, you can also add a note, which will be displayed above the log data. Some examples:
158 |
@milo Was this you?
159 | #etsy LOL, wut?
160 |
161 |
162 |
167 |
168 |
171 |
172 |
175 |
176 |
179 |
180 |
248 |
249 |
250 |
251 |
252 |
--------------------------------------------------------------------------------
/static/js/all.jsc:
--------------------------------------------------------------------------------
1 | jquery.js
2 | jquery-ui-1.8.16.custom.min.js
3 | date.format.js
4 | jstorage.js
5 | templating.js
6 | sha-256.js
7 | guiders-1.2.0.js
8 | supergrep.js
9 | page.js
10 | guide.js
11 |
--------------------------------------------------------------------------------
/static/js/all.min.jsc:
--------------------------------------------------------------------------------
1 | jquery.min.js
2 | jquery-ui-1.8.16.custom.min.js
3 | date.format.min.js
4 | jstorage.min.js
5 | templating.min.js
6 | sha-256.min.js
7 | guiders-1.2.0.min.js
8 | supergrep.min.js
9 | page.min.js
10 | guide.min.js
11 |
--------------------------------------------------------------------------------
/static/js/date.format.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Date Format 1.2.3
3 | * (c) 2007-2009 Steven Levithan
4 | * MIT license
5 | *
6 | * Includes enhancements by Scott Trenda
7 | * and Kris Kowal
8 | *
9 | * Accepts a date, a mask, or a date and a mask.
10 | * Returns a formatted version of the given date.
11 | * The date defaults to the current date/time.
12 | * The mask defaults to dateFormat.masks.default.
13 | */
14 |
15 | var dateFormat = function () {
16 | var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
17 | timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[\-+]\d{4})?)\b/g,
18 | timezoneClip = /[^\-+\dA-Z]/g,
19 | pad = function (val, len) {
20 | val = String(val);
21 | len = len || 2;
22 | while (val.length < len) val = "0" + val;
23 | return val;
24 | };
25 |
26 | // Regexes and supporting functions are cached through closure
27 | return function (date, mask, utc) {
28 | var dF = dateFormat;
29 |
30 | // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
31 | if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
32 | mask = date;
33 | date = undefined;
34 | }
35 |
36 | // Passing date through Date applies Date.parse, if necessary
37 | date = date ? new Date(date) : new Date();
38 | if (isNaN(date)) throw SyntaxError("invalid date");
39 |
40 | mask = String(dF.masks[mask] || mask || dF.masks["default"]);
41 |
42 | // Allow setting the utc argument via the mask
43 | if (mask.slice(0, 4) == "UTC:") {
44 | mask = mask.slice(4);
45 | utc = true;
46 | }
47 |
48 | var _ = utc ? "getUTC" : "get",
49 | d = date[_ + "Date"](),
50 | D = date[_ + "Day"](),
51 | m = date[_ + "Month"](),
52 | y = date[_ + "FullYear"](),
53 | H = date[_ + "Hours"](),
54 | M = date[_ + "Minutes"](),
55 | s = date[_ + "Seconds"](),
56 | L = date[_ + "Milliseconds"](),
57 | o = utc ? 0 : date.getTimezoneOffset(),
58 | flags = {
59 | d: d,
60 | dd: pad(d),
61 | ddd: dF.i18n.dayNames[D],
62 | dddd: dF.i18n.dayNames[D + 7],
63 | m: m + 1,
64 | mm: pad(m + 1),
65 | mmm: dF.i18n.monthNames[m],
66 | mmmm: dF.i18n.monthNames[m + 12],
67 | yy: String(y).slice(2),
68 | yyyy: y,
69 | h: H % 12 || 12,
70 | hh: pad(H % 12 || 12),
71 | H: H,
72 | HH: pad(H),
73 | M: M,
74 | MM: pad(M),
75 | s: s,
76 | ss: pad(s),
77 | l: pad(L, 3),
78 | L: pad(L > 99 ? Math.round(L / 10) : L),
79 | t: H < 12 ? "a" : "p",
80 | tt: H < 12 ? "am" : "pm",
81 | T: H < 12 ? "A" : "P",
82 | TT: H < 12 ? "AM" : "PM",
83 | Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
84 | o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
85 | S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
86 | };
87 |
88 | return mask.replace(token, function ($0) {
89 | return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
90 | });
91 | };
92 | }();
93 |
94 | // Some common format strings
95 | dateFormat.masks = {
96 | "default": "ddd mmm dd yyyy HH:MM:ss",
97 | shortDate: "m/d/yy",
98 | mediumDate: "mmm d, yyyy",
99 | longDate: "mmmm d, yyyy",
100 | fullDate: "dddd, mmmm d, yyyy",
101 | shortTime: "h:MM TT",
102 | mediumTime: "h:MM:ss TT",
103 | longTime: "h:MM:ss TT Z",
104 | isoDate: "yyyy-mm-dd",
105 | isoTime: "HH:MM:ss",
106 | isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
107 | isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
108 | };
109 |
110 | // Internationalization strings
111 | dateFormat.i18n = {
112 | dayNames: [
113 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
114 | "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
115 | ],
116 | monthNames: [
117 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
118 | "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
119 | ]
120 | };
121 |
122 | // For convenience...
123 | Date.prototype.format = function (mask, utc) {
124 | return dateFormat(this, mask, utc);
125 | };
126 |
--------------------------------------------------------------------------------
/static/js/date.format.min.js:
--------------------------------------------------------------------------------
1 | var dateFormat=function(){var j=/d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,k=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[\-+]\d{4})?)\b/g,r=/[^\-+\dA-Z]/g,d=function(a,c){a=""+a;for(c=c||2;a.lengthe?"a":"p",tt:12>e?"am":"pm",T:12>e?"A":"P",TT:12>e?"AM":"PM",Z:h?"UTC":((""+a).match(k)||[""]).pop().replace(r,""),o:(0 Try checking and unchecking the 'Reverse sort' options to see how it affects the display of log entries.
Click 'Next' when you are ready to continue.",attachTo:"#view-options",position:5,buttons:[{name:"Next"}],next:"guide_viewoptions_max"}),guiders.createGuider({id:"guide_viewoptions_max",title:"Max entries",description:"This value is the maximum number of log entries that will be displayed. Once that limit is reached, older log entries are removed.
Try entering '3' into the box then press the 'tab' key to see how it affects the display of log entries.
Click 'Next' when you are ready to continue.",attachTo:"#view-options",position:5,buttons:[{name:"Next",onclick:function(){a("#max-entries").val(500).trigger("change"),Etsy.Supergrep.clearEntries(),guiders.next()}}],next:"guide_highlight"}),guiders.createGuider({id:"guide_highlight",title:"Highlighting",description:"This field allows you to highlight log entries based on matching terms or a regex.",attachTo:"#highlight-options",position:6,buttons:[{name:"Next"}],next:"guide_highlight_help"}),guiders.createGuider({id:"guide_highlight_help",title:"Highlighting",description:"This link will show you the formats allowed for highlighting rules.",attachTo:"#highlight-rule-help",position:6,buttons:[{name:"Next",onclick:function(){a("#highlight-rule-help").trigger("click"),guiders.next()}}],next:"guide_highlight_help_show"}),guiders.createGuider({id:"guide_highlight_help_show",title:"Highlighting Rules",description:"This help dialog shows you how to format your hightlighting and filtering rules.",attachTo:"#rule-help-dialog",position:9,width:300,buttons:[{name:"Next",onclick:function(){a("#rule-help-dialog").dialog("close"),Etsy.Supergrep.writeLogEntries(e()),a("#highlight-log").val("debug, warning").trigger("change"),guiders.next()}}],next:"guide_highlight_example1"}),guiders.createGuider({id:"guide_highlight_example1",title:"Highlighting Example #1",description:"Here we see what happens when we highlight using the keywords 'debug' and 'warning'.",attachTo:"#highlight-log",position:10,buttons:[{name:"Next",onclick:function(){a("#rule-help-dialog").dialog("close"),a("#highlight-log").val("/(debug|warning)/i").trigger("change"),guiders.next()}}],next:"guide_highlight_example2"}),guiders.createGuider({id:"guide_highlight_example2",title:"Highlighting Example #2",description:"Here is the same rule expressed as a regex.",attachTo:"#highlight-log",position:10,buttons:[{name:"Next",onclick:function(){a("#rule-help-dialog").dialog("close"),a("#highlight-log").val("").trigger("change"),guiders.next()}}],next:"guide_filter"}),guiders.createGuider({id:"guide_filter",title:"Filtering",description:"Filtering works just like highlighting except instead of coloring matching entries, it hides them.",attachTo:"#filter-log",position:10,buttons:[{name:"Show me",onclick:function(){a("#filter-log").val("errorhandler").trigger("change"),guiders.next()}}],next:"guide_filter_example"}),guiders.createGuider({id:"guide_filter_example",title:"Filter Example",description:"Here we are filtering any log entries that contain 'errorhandler' (case insensitive). Notice how some of the log entries are now gone.",attachTo:"#clear-field-filter",position:10,buttons:[{name:"Next"}],next:"guide_filter_clear"}),guiders.createGuider({id:"guide_filter_clear",title:"Clearing Rules",description:"Highlighting and filtering rules can be easily cleared by clicking the '[X]' button. Try it now!",attachTo:"#clear-field-filter",position:5,offset:{top:0,left:35},buttons:[{name:"Next",onclick:function(){a("#filter-log").val("").trigger("change"),guiders.next()}}],next:"guide_clearlog"}),guiders.createGuider({id:"guide_clearlog",title:"Clearing Log Entries",description:"If you ever need to clear the log entries, just click this button. Try it now!",attachTo:"#clear-log",position:6,buttons:[{name:"Next",onclick:function(){Etsy.Supergrep.clearEntries(),guiders.next()}}],next:"guide_intrologs"}),guiders.createGuider({id:"guide_intrologs",title:"Log Entries",description:"Next, you are going to be introduced to a few features on the log entries themselves and then you are done!",overlay:!0,buttons:[{name:"Next",onclick:function(){a("#sort-order").attr("checked",!1).trigger("change"),a("#highlight-log").val("").trigger("change"),a("#filter-log").val("").trigger("change"),a("#max-entries").val(500).trigger("change"),Etsy.Supergrep.writeLogEntries(e()),guiders.next()}}],next:"guide_rawlog"}),guiders.createGuider({id:"guide_rawlog",title:"Raw Logs",description:"If you ever need to see the original, raw log entry, just click the 'Raw Log' link.",attachTo:".rawlog-link:first",position:6,buttons:[{name:"Next",onclick:function(){var b=a(".rawlog-link:first").parent("li.log").attr("id");a("#"+b+" .rawdata").show(),guiders.next()}}],next:"guide_rawlog_show"}),guiders.createGuider({id:"guide_rawlog_show",title:"Raw Logs",description:"This is the raw log data, exactly is it was recorded.",attachTo:".rawdata:first",position:6,buttons:[{name:"Next",onclick:function(){var b=a(".rawlog-link:first").parent("li.log").attr("id");a("#"+b+" .rawdata").hide(),guiders.next()}}],next:"guide_stacktrace"}),guiders.createGuider({id:"guide_stacktrace",title:"Stack Trace",description:"If the log entry contains a recognized stack trace, this link will appear.",attachTo:".stacktrace-link:first",position:6,buttons:[{name:"Next",onclick:function(){var b=a(".stacktrace-link:first").parent("li.log").attr("id");a("#"+b+" .stacktrace").show(),guiders.next()}}],next:"guide_stacktrace_show"}),guiders.createGuider({id:"guide_stacktrace_show",title:"Stack Trace",description:"This is the parsed stack trace, showing call depth, function call + params, and a link to the source file and line number in Github.",attachTo:".stacktrace:first",position:7,buttons:[{name:"Next",onclick:function(){var b=a(".rawlog-link:first").parent("li.log").attr("id");guiders.next()}}],next:"guide_export"}),guiders.createGuider({id:"guide_export",title:"Exporting Data",description:"One last thing before we finish.
These links allow you to send a raw log or stack trace to either a gist or to an IRC channel/nick. For more info on how that works, click the '[?]' link.",attachTo:".stacktrace:first .gist:first",position:5,buttons:[{name:"Next",onclick:function(){Etsy.Supergrep.clearEntries(),guiders.next()}}],next:"guide_done"}),guiders.createGuider({id:"guide_done",title:"End of Tour",description:"That's about it. Have fun!",overlay:!0,buttons:[{name:"Next"}],next:"guide_restart"}),guiders.createGuider({id:"guide_restart",title:"Viewing this guide",description:"BTW, if you ever want to view this guide again, just click this button.",attachTo:"#guide-help",position:6,buttons:[{name:"Close",onclick:function(){guiders.hideAll(),j()}}]}),guiders.createGuider({id:"guide_restart_abort",title:"Viewing this guide",description:"BTW, if you ever want to view this guide again, just click this button.",attachTo:"#guide-help",position:6,buttons:[{name:"Close",onclick:function(){guiders.hideAll()}}]});var f="supergreptour",g=c(f);b(f,1,365);if(g)return;var h;guiders.show("guide_welcome")});
--------------------------------------------------------------------------------
/static/js/guiders-1.2.0.js:
--------------------------------------------------------------------------------
1 | /**
2 | * guiders.js
3 | *
4 | * version 1.2.0
5 | *
6 | * Developed at Optimizely. (www.optimizely.com)
7 | * We make A/B testing you'll actually use.
8 | *
9 | * Released under the Apache License 2.0.
10 | * www.apache.org/licenses/LICENSE-2.0.html
11 | *
12 | * Questions about Guiders or Optimizely?
13 | * Email us at jeff+pickhardt@optimizely.com or hello@optimizely.com.
14 | *
15 | * Enjoy!
16 | */
17 |
18 | var guiders = (function($) {
19 | var guiders = {};
20 |
21 | guiders.version = "1.2.0";
22 |
23 | guiders._defaultSettings = {
24 | attachTo: null,
25 | buttons: [{name: "Close"}],
26 | buttonCustomHTML: "",
27 | classString: null,
28 | description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
29 | highlight: null,
30 | isHashable: true,
31 | offset: {
32 | top: null,
33 | left: null
34 | },
35 | onShow: null,
36 | overlay: false,
37 | position: 0, // 1-12 follows an analog clock, 0 means centered
38 | title: "Sample title goes here",
39 | width: 400,
40 | xButton: false // this places a closer "x" button in the top right of the guider
41 | };
42 |
43 | guiders._htmlSkeleton = [
44 | "
",
45 | "
",
46 | " ",
47 | " ",
48 | " ",
49 | "
",
50 | "
",
51 | "
",
52 | "
",
53 | "
",
54 | "
"
55 | ].join("");
56 |
57 | guiders._arrowSize = 42; // = arrow's width and height
58 | guiders._closeButtonTitle = "Close";
59 | guiders._currentGuiderID = null;
60 | guiders._guiders = {};
61 | guiders._lastCreatedGuiderID = null;
62 | guiders._nextButtonTitle = "Next";
63 | guiders._zIndexForHighlight = 101;
64 |
65 | guiders._addButtons = function(myGuider) {
66 | // Add buttons
67 | var guiderButtonsContainer = myGuider.elem.find(".guider_buttons");
68 |
69 | if (myGuider.buttons === null || myGuider.buttons.length === 0) {
70 | guiderButtonsContainer.remove();
71 | return;
72 | }
73 |
74 | for (var i = myGuider.buttons.length-1; i >= 0; i--) {
75 | var thisButton = myGuider.buttons[i];
76 | var thisButtonElem = $("", {
77 | "class" : "guider_button",
78 | "text" : thisButton.name });
79 | if (typeof thisButton.classString !== "undefined" && thisButton.classString !== null) {
80 | thisButtonElem.addClass(thisButton.classString);
81 | }
82 |
83 | guiderButtonsContainer.append(thisButtonElem);
84 |
85 | if (thisButton.onclick) {
86 | thisButtonElem.bind("click", thisButton.onclick);
87 | } else if (!thisButton.onclick &&
88 | thisButton.name.toLowerCase() === guiders._closeButtonTitle.toLowerCase()) {
89 | thisButtonElem.bind("click", function() { guiders.hideAll(); });
90 | } else if (!thisButton.onclick &&
91 | thisButton.name.toLowerCase() === guiders._nextButtonTitle.toLowerCase()) {
92 | thisButtonElem.bind("click", function() { guiders.next(); });
93 | }
94 | }
95 |
96 | if (myGuider.buttonCustomHTML !== "") {
97 | var myCustomHTML = $(myGuider.buttonCustomHTML);
98 | myGuider.elem.find(".guider_buttons").append(myCustomHTML);
99 | }
100 |
101 | if (myGuider.buttons.length == 0) {
102 | guiderButtonsContainer.remove();
103 | }
104 | };
105 |
106 | guiders._addXButton = function(myGuider) {
107 | var xButtonContainer = myGuider.elem.find(".guider_close");
108 | var xButton = $("", {
109 | "class" : "x_button",
110 | "role" : "button" });
111 | xButtonContainer.append(xButton);
112 | xButton.click(function() { guiders.hideAll(); });
113 | };
114 |
115 | guiders._attach = function(myGuider) {
116 | if (myGuider === null) {
117 | return;
118 | }
119 |
120 | var myHeight = myGuider.elem.innerHeight();
121 | var myWidth = myGuider.elem.innerWidth();
122 |
123 | if (myGuider.position === 0 || myGuider.attachTo === null) {
124 | myGuider.elem.css("position", "absolute");
125 | myGuider.elem.css("top", ($(window).height() - myHeight) / 3 + $(window).scrollTop() + "px");
126 | myGuider.elem.css("left", ($(window).width() - myWidth) / 2 + $(window).scrollLeft() + "px");
127 | return;
128 | }
129 |
130 | var attachTo = $(myGuider.attachTo);
131 | if (!attachTo.length) {
132 | return;
133 | }
134 | myGuider.attachTo = attachTo;
135 | var base = myGuider.attachTo.offset();
136 | var attachToHeight = myGuider.attachTo.innerHeight();
137 | var attachToWidth = myGuider.attachTo.innerWidth();
138 |
139 | var top = base.top;
140 | var left = base.left;
141 |
142 | var bufferOffset = 0.9 * guiders._arrowSize;
143 |
144 | var offsetMap = { // Follows the form: [height, width]
145 | 1: [-bufferOffset - myHeight, attachToWidth - myWidth],
146 | 2: [0, bufferOffset + attachToWidth],
147 | 3: [attachToHeight/2 - myHeight/2, bufferOffset + attachToWidth],
148 | 4: [attachToHeight - myHeight, bufferOffset + attachToWidth],
149 | 5: [bufferOffset + attachToHeight, attachToWidth - myWidth],
150 | 6: [bufferOffset + attachToHeight, attachToWidth/2 - myWidth/2],
151 | 7: [bufferOffset + attachToHeight, 0],
152 | 8: [attachToHeight - myHeight, -myWidth - bufferOffset],
153 | 9: [attachToHeight/2 - myHeight/2, -myWidth - bufferOffset],
154 | 10: [0, -myWidth - bufferOffset],
155 | 11: [-bufferOffset - myHeight, 0],
156 | 12: [-bufferOffset - myHeight, attachToWidth/2 - myWidth/2]
157 | };
158 |
159 | offset = offsetMap[myGuider.position];
160 | top += offset[0];
161 | left += offset[1];
162 |
163 | if (myGuider.offset.top !== null) {
164 | top += myGuider.offset.top;
165 | }
166 |
167 | if (myGuider.offset.left !== null) {
168 | left += myGuider.offset.left;
169 | }
170 |
171 | myGuider.elem.css({
172 | "position": "absolute",
173 | "top": top,
174 | "left": left
175 | });
176 | };
177 |
178 | guiders._guiderById = function(id) {
179 | if (typeof guiders._guiders[id] === "undefined") {
180 | throw "Cannot find guider with id " + id;
181 | }
182 | return guiders._guiders[id];
183 | };
184 |
185 | guiders._showOverlay = function() {
186 | $("#guider_overlay").fadeIn("fast", function(){
187 | if (this.style.removeAttribute) {
188 | this.style.removeAttribute("filter");
189 | }
190 | });
191 | // This callback is needed to fix an IE opacity bug.
192 | // See also:
193 | // http://www.kevinleary.net/jquery-fadein-fadeout-problems-in-internet-explorer/
194 | };
195 |
196 | guiders._highlightElement = function(selector) {
197 | $(selector).css({'z-index': guiders._zIndexForHighlight});
198 | };
199 |
200 | guiders._dehighlightElement = function(selector) {
201 | $(selector).css({'z-index': 1});
202 | };
203 |
204 | guiders._hideOverlay = function() {
205 | $("#guider_overlay").fadeOut("fast");
206 | };
207 |
208 | guiders._initializeOverlay = function() {
209 | if ($("#guider_overlay").length === 0) {
210 | $("").hide().appendTo("body");
211 | }
212 | };
213 |
214 | guiders._styleArrow = function(myGuider) {
215 | var position = myGuider.position || 0;
216 | if (!position) {
217 | return;
218 | }
219 | var myGuiderArrow = $(myGuider.elem.find(".guider_arrow"));
220 | var newClass = {
221 | 1: "guider_arrow_down",
222 | 2: "guider_arrow_left",
223 | 3: "guider_arrow_left",
224 | 4: "guider_arrow_left",
225 | 5: "guider_arrow_up",
226 | 6: "guider_arrow_up",
227 | 7: "guider_arrow_up",
228 | 8: "guider_arrow_right",
229 | 9: "guider_arrow_right",
230 | 10: "guider_arrow_right",
231 | 11: "guider_arrow_down",
232 | 12: "guider_arrow_down"
233 | };
234 | myGuiderArrow.addClass(newClass[position]);
235 |
236 | var myHeight = myGuider.elem.innerHeight();
237 | var myWidth = myGuider.elem.innerWidth();
238 | var arrowOffset = guiders._arrowSize / 2;
239 | var positionMap = {
240 | 1: ["right", arrowOffset],
241 | 2: ["top", arrowOffset],
242 | 3: ["top", myHeight/2 - arrowOffset],
243 | 4: ["bottom", arrowOffset],
244 | 5: ["right", arrowOffset],
245 | 6: ["left", myWidth/2 - arrowOffset],
246 | 7: ["left", arrowOffset],
247 | 8: ["bottom", arrowOffset],
248 | 9: ["top", myHeight/2 - arrowOffset],
249 | 10: ["top", arrowOffset],
250 | 11: ["left", arrowOffset],
251 | 12: ["left", myWidth/2 - arrowOffset]
252 | };
253 | var position = positionMap[myGuider.position];
254 | myGuiderArrow.css(position[0], position[1] + "px");
255 | };
256 |
257 | /**
258 | * One way to show a guider to new users is to direct new users to a URL such as
259 | * http://www.mysite.com/myapp#guider=welcome
260 | *
261 | * This can also be used to run guiders on multiple pages, by redirecting from
262 | * one page to another, with the guider id in the hash tag.
263 | *
264 | * Alternatively, if you use a session variable or flash messages after sign up,
265 | * you can add selectively add JavaScript to the page: "guiders.show('first');"
266 | */
267 | guiders._showIfHashed = function(myGuider) {
268 | var GUIDER_HASH_TAG = "guider=";
269 | var hashIndex = window.location.hash.indexOf(GUIDER_HASH_TAG);
270 | if (hashIndex !== -1) {
271 | var hashGuiderId = window.location.hash.substr(hashIndex + GUIDER_HASH_TAG.length);
272 | if (myGuider.id.toLowerCase() === hashGuiderId.toLowerCase()) {
273 | // Success!
274 | guiders.show(myGuider.id);
275 | }
276 | }
277 | };
278 |
279 | guiders.next = function() {
280 | var currentGuider = guiders._guiders[guiders._currentGuiderID];
281 | if (typeof currentGuider === "undefined") {
282 | return;
283 | }
284 | var nextGuiderId = currentGuider.next || null;
285 | if (nextGuiderId !== null && nextGuiderId !== "") {
286 | var myGuider = guiders._guiderById(nextGuiderId);
287 | var omitHidingOverlay = myGuider.overlay ? true : false;
288 | guiders.hideAll(omitHidingOverlay);
289 | if (currentGuider.highlight) {
290 | guiders._dehighlightElement(currentGuider.highlight);
291 | }
292 | guiders.show(nextGuiderId);
293 | }
294 | };
295 |
296 | guiders.createGuider = function(passedSettings) {
297 | if (passedSettings === null || passedSettings === undefined) {
298 | passedSettings = {};
299 | }
300 |
301 | // Extend those settings with passedSettings
302 | myGuider = $.extend({}, guiders._defaultSettings, passedSettings);
303 | myGuider.id = myGuider.id || String(Math.floor(Math.random() * 1000));
304 |
305 | var guiderElement = $(guiders._htmlSkeleton);
306 | myGuider.elem = guiderElement;
307 | if (typeof myGuider.classString !== "undefined" && myGuider.classString !== null) {
308 | myGuider.elem.addClass(myGuider.classString);
309 | }
310 | myGuider.elem.css("width", myGuider.width + "px");
311 |
312 | var guiderTitleContainer = guiderElement.find(".guider_title");
313 | guiderTitleContainer.html(myGuider.title);
314 |
315 | guiderElement.find(".guider_description").html(myGuider.description);
316 |
317 | guiders._addButtons(myGuider);
318 |
319 | if (myGuider.xButton) {
320 | guiders._addXButton(myGuider);
321 | }
322 |
323 | guiderElement.hide();
324 | guiderElement.appendTo("body");
325 | guiderElement.attr("id", myGuider.id);
326 |
327 | // Ensure myGuider.attachTo is a jQuery element.
328 | if (typeof myGuider.attachTo !== "undefined" && myGuider !== null) {
329 | guiders._attach(myGuider);
330 | guiders._styleArrow(myGuider);
331 | }
332 |
333 | guiders._initializeOverlay();
334 |
335 | guiders._guiders[myGuider.id] = myGuider;
336 | guiders._lastCreatedGuiderID = myGuider.id;
337 |
338 | /**
339 | * If the URL of the current window is of the form
340 | * http://www.myurl.com/mypage.html#guider=id
341 | * then show this guider.
342 | */
343 | if (myGuider.isHashable) {
344 | guiders._showIfHashed(myGuider);
345 | }
346 |
347 | return guiders;
348 | };
349 |
350 | guiders.hideAll = function(omitHidingOverlay) {
351 | $(".guider").fadeOut("fast");
352 | if (typeof omitHidingOverlay !== "undefined" && omitHidingOverlay === true) {
353 | // do nothing for now
354 | } else {
355 | guiders._hideOverlay();
356 | }
357 | return guiders;
358 | };
359 |
360 | guiders.show = function(id) {
361 | if (!id && guiders._lastCreatedGuiderID) {
362 | id = guiders._lastCreatedGuiderID;
363 | }
364 |
365 | var myGuider = guiders._guiderById(id);
366 | if (myGuider.overlay) {
367 | guiders._showOverlay();
368 | // if guider is attached to an element, make sure it's visible
369 | if (myGuider.highlight) {
370 | guiders._highlightElement(myGuider.highlight);
371 | }
372 | }
373 |
374 | guiders._attach(myGuider);
375 |
376 | // You can use an onShow function to take some action before the guider is shown.
377 | if (myGuider.onShow) {
378 | myGuider.onShow(myGuider);
379 | }
380 |
381 | myGuider.elem.fadeIn("fast");
382 |
383 | var windowHeight = $(window).height();
384 | var scrollHeight = $(window).scrollTop();
385 | var guiderOffset = myGuider.elem.offset();
386 | var guiderElemHeight = myGuider.elem.height();
387 |
388 | if (guiderOffset.top - scrollHeight < 0 ||
389 | guiderOffset.top + guiderElemHeight + 40 > scrollHeight + windowHeight) {
390 | window.scrollTo(0, Math.max(guiderOffset.top + (guiderElemHeight / 2) - (windowHeight / 2), 0));
391 | }
392 |
393 | guiders._currentGuiderID = id;
394 | return guiders;
395 | };
396 |
397 | return guiders;
398 | }).call(this, jQuery);
399 |
--------------------------------------------------------------------------------
/static/js/guiders-1.2.0.min.js:
--------------------------------------------------------------------------------
1 | var guiders=function(g){var b={version:"1.2.0",_defaultSettings:{attachTo:null,buttons:[{name:"Close"}],buttonCustomHTML:"",classString:null,description:"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", highlight:null,isHashable:!0,offset:{top:null,left:null},onShow:null,overlay:!1,position:0,title:"Sample title goes here",width:400,xButton:!1},_htmlSkeleton:"
",_arrowSize:42,_closeButtonTitle:"Close",_currentGuiderID:null,_guiders:{},_lastCreatedGuiderID:null, _nextButtonTitle:"Next",_zIndexForHighlight:101};b._addButtons=function(a){var c=a.elem.find(".guider_buttons");if(null===a.buttons||0===a.buttons.length)c.remove();else{for(var f=a.buttons.length-1;0<=f;f--){var e=a.buttons[f],d=g("",{"class":"guider_button",text:e.name});"undefined"!==typeof e.classString&&null!==e.classString&&d.addClass(e.classString);c.append(d);e.onclick?d.bind("click",e.onclick):!e.onclick&&e.name.toLowerCase()===b._closeButtonTitle.toLowerCase()?d.bind("click",function(){b.hideAll()}): !e.onclick&&e.name.toLowerCase()===b._nextButtonTitle.toLowerCase()&&d.bind("click",function(){b.next()})}""!==a.buttonCustomHTML&&(f=g(a.buttonCustomHTML),a.elem.find(".guider_buttons").append(f));0==a.buttons.length&&c.remove()}};b._addXButton=function(a){var a=a.elem.find(".guider_close"),c=g("",{"class":"x_button",role:"button"});a.append(c);c.click(function(){b.hideAll()})};b._attach=function(a){if(null!==a){var c=a.elem.innerHeight(),f=a.elem.innerWidth();if(0===a.position||null=== a.attachTo)a.elem.css("position","absolute"),a.elem.css("top",(g(window).height()-c)/3+g(window).scrollTop()+"px"),a.elem.css("left",(g(window).width()-f)/2+g(window).scrollLeft()+"px");else{var e=g(a.attachTo);if(e.length){a.attachTo=e;var d=a.attachTo.offset(),e=a.attachTo.innerHeight(),i=a.attachTo.innerWidth(),j=d.top,d=d.left,h=0.9*b._arrowSize;offset={1:[-h-c,i-f],2:[0,h+i],3:[e/2-c/2,h+i],4:[e-c,h+i],5:[h+e,i-f],6:[h+e,i/2-f/2],7:[h+e,0],8:[e-c,-f-h],9:[e/2-c/2,-f-h],10:[0,-f-h],11:[-h-c,0], 12:[-h-c,i/2-f/2]}[a.position];j+=offset[0];d+=offset[1];null!==a.offset.top&&(j+=a.offset.top);null!==a.offset.left&&(d+=a.offset.left);a.elem.css({position:"absolute",top:j,left:d})}}}};b._guiderById=function(a){if("undefined"===typeof b._guiders[a])throw"Cannot find guider with id "+a;return b._guiders[a]};b._showOverlay=function(){g("#guider_overlay").fadeIn("fast",function(){this.style.removeAttribute&&this.style.removeAttribute("filter")})};b._highlightElement=function(a){g(a).css({"z-index":b._zIndexForHighlight})}; b._dehighlightElement=function(a){g(a).css({"z-index":1})};b._hideOverlay=function(){g("#guider_overlay").fadeOut("fast")};b._initializeOverlay=function(){0===g("#guider_overlay").length&&g('').hide().appendTo("body")};b._styleArrow=function(a){var c=a.position||0;if(c){var f=g(a.elem.find(".guider_arrow"));f.addClass({1:"guider_arrow_down",2:"guider_arrow_left",3:"guider_arrow_left",4:"guider_arrow_left",5:"guider_arrow_up",6:"guider_arrow_up",7:"guider_arrow_up",8:"guider_arrow_right", 9:"guider_arrow_right",10:"guider_arrow_right",11:"guider_arrow_down",12:"guider_arrow_down"}[c]);var c=a.elem.innerHeight(),e=a.elem.innerWidth(),d=b._arrowSize/2,c={1:["right",d],2:["top",d],3:["top",c/2-d],4:["bottom",d],5:["right",d],6:["left",e/2-d],7:["left",d],8:["bottom",d],9:["top",c/2-d],10:["top",d],11:["left",d],12:["left",e/2-d]}[a.position];f.css(c[0],c[1]+"px")}};b._showIfHashed=function(a){var c=window.location.hash.indexOf("guider=");-1!==c&&(c=window.location.hash.substr(c+7),a.id.toLowerCase()=== c.toLowerCase()&&b.show(a.id))};b.next=function(){var a=b._guiders[b._currentGuiderID];if("undefined"!==typeof a){var c=a.next||null;if(null!==c&&""!==c){var f=b._guiderById(c).overlay?!0:!1;b.hideAll(f);a.highlight&&b._dehighlightElement(a.highlight);b.show(c)}}};b.createGuider=function(a){if(null===a||void 0===a)a={};myGuider=g.extend({},b._defaultSettings,a);myGuider.id=myGuider.id||""+Math.floor(1E3*Math.random());a=g(b._htmlSkeleton);myGuider.elem=a;"undefined"!==typeof myGuider.classString&& null!==myGuider.classString&&myGuider.elem.addClass(myGuider.classString);myGuider.elem.css("width",myGuider.width+"px");a.find(".guider_title").html(myGuider.title);a.find(".guider_description").html(myGuider.description);b._addButtons(myGuider);myGuider.xButton&&b._addXButton(myGuider);a.hide();a.appendTo("body");a.attr("id",myGuider.id);"undefined"!==typeof myGuider.attachTo&&null!==myGuider&&(b._attach(myGuider),b._styleArrow(myGuider));b._initializeOverlay();b._guiders[myGuider.id]=myGuider; b._lastCreatedGuiderID=myGuider.id;myGuider.isHashable&&b._showIfHashed(myGuider);return b};b.hideAll=function(a){g(".guider").fadeOut("fast");"undefined"!==typeof a&&!0===a||b._hideOverlay();return b};b.show=function(a){if(!a&&b._lastCreatedGuiderID)a=b._lastCreatedGuiderID;var c=b._guiderById(a);c.overlay&&(b._showOverlay(),c.highlight&&b._highlightElement(c.highlight));b._attach(c);if(c.onShow)c.onShow(c);c.elem.fadeIn("fast");var f=g(window).height(),e=g(window).scrollTop(),d=c.elem.offset(), c=c.elem.height();(0>d.top-e||d.top+c+40>e+f)&&window.scrollTo(0,Math.max(d.top+c/2-f/2,0));b._currentGuiderID=a;return b};return b}.call(this,jQuery);
--------------------------------------------------------------------------------
/static/js/jstorage.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ----------------------------- JSTORAGE -------------------------------------
3 | * Simple local storage wrapper to save data on the browser side, supporting
4 | * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
5 | *
6 | * Copyright (c) 2010 Andris Reinman, andris.reinman@gmail.com
7 | * Project homepage: www.jstorage.info
8 | *
9 | * Licensed under MIT-style license:
10 | *
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy
12 | * of this software and associated documentation files (the "Software"), to deal
13 | * in the Software without restriction, including without limitation the rights
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | * copies of the Software, and to permit persons to whom the Software is
16 | * furnished to do so, subject to the following conditions:
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | * SOFTWARE.
25 | */
26 |
27 | /**
28 | * $.jStorage
29 | *
30 | * USAGE:
31 | *
32 | * jStorage requires Prototype, MooTools or jQuery! If jQuery is used, then
33 | * jQuery-JSON (http://code.google.com/p/jquery-json/) is also needed.
34 | * (jQuery-JSON needs to be loaded BEFORE jStorage!)
35 | *
36 | * Methods:
37 | *
38 | * -set(key, value)
39 | * $.jStorage.set(key, value) -> saves a value
40 | *
41 | * -get(key[, default])
42 | * value = $.jStorage.get(key [, default]) ->
43 | * retrieves value if key exists, or default if it doesn't
44 | *
45 | * -deleteKey(key)
46 | * $.jStorage.deleteKey(key) -> removes a key from the storage
47 | *
48 | * -flush()
49 | * $.jStorage.flush() -> clears the cache
50 | *
51 | * -storageObj()
52 | * $.jStorage.storageObj() -> returns a read-ony copy of the actual storage
53 | *
54 | * -storageSize()
55 | * $.jStorage.storageSize() -> returns the size of the storage in bytes
56 | *
57 | * -index()
58 | * $.jStorage.index() -> returns the used keys as an array
59 | *
60 | * -storageAvailable()
61 | * $.jStorage.storageAvailable() -> returns true if storage is available
62 | *
63 | * -reInit()
64 | * $.jStorage.reInit() -> reloads the data from browser storage
65 | *
66 | * can be any JSON-able value, including objects and arrays.
67 | *
68 | **/
69 |
70 | (function($){
71 | if(!$ || !($.toJSON || Object.toJSON || window.JSON)){
72 | throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!");
73 | }
74 |
75 | var
76 | /* This is the object, that holds the cached values */
77 | _storage = {},
78 |
79 | /* Actual browser storage (localStorage or globalStorage['domain']) */
80 | _storage_service = {jStorage:"{}"},
81 |
82 | /* DOM element for older IE versions, holds userData behavior */
83 | _storage_elm = null,
84 |
85 | /* How much space does the storage take */
86 | _storage_size = 0,
87 |
88 | /* function to encode objects to JSON strings */
89 | json_encode = $.toJSON || Object.toJSON || (window.JSON && (JSON.encode || JSON.stringify)),
90 |
91 | /* function to decode objects from JSON strings */
92 | json_decode = $.evalJSON || (window.JSON && (JSON.decode || JSON.parse)) || function(str){
93 | return String(str).evalJSON();
94 | },
95 |
96 | /* which backend is currently used */
97 | _backend = false,
98 |
99 | /* Next check for TTL */
100 | _ttl_timeout,
101 |
102 | /**
103 | * XML encoding and decoding as XML nodes can't be JSON'ized
104 | * XML nodes are encoded and decoded if the node is the value to be saved
105 | * but not if it's as a property of another object
106 | * Eg. -
107 | * $.jStorage.set("key", xmlNode); // IS OK
108 | * $.jStorage.set("key", {xml: xmlNode}); // NOT OK
109 | */
110 | _XMLService = {
111 |
112 | /**
113 | * Validates a XML node to be XML
114 | * based on jQuery.isXML function
115 | */
116 | isXML: function(elm){
117 | var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
118 | return documentElement ? documentElement.nodeName !== "HTML" : false;
119 | },
120 |
121 | /**
122 | * Encodes a XML node to string
123 | * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
124 | */
125 | encode: function(xmlNode) {
126 | if(!this.isXML(xmlNode)){
127 | return false;
128 | }
129 | try{ // Mozilla, Webkit, Opera
130 | return new XMLSerializer().serializeToString(xmlNode);
131 | }catch(E1) {
132 | try { // IE
133 | return xmlNode.xml;
134 | }catch(E2){}
135 | }
136 | return false;
137 | },
138 |
139 | /**
140 | * Decodes a XML node from string
141 | * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
142 | */
143 | decode: function(xmlString){
144 | var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
145 | (window.ActiveXObject && function(_xmlString) {
146 | var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
147 | xml_doc.async = 'false';
148 | xml_doc.loadXML(_xmlString);
149 | return xml_doc;
150 | }),
151 | resultXML;
152 | if(!dom_parser){
153 | return false;
154 | }
155 | resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
156 | return this.isXML(resultXML)?resultXML:false;
157 | }
158 | };
159 |
160 | ////////////////////////// PRIVATE METHODS ////////////////////////
161 |
162 | /**
163 | * Initialization function. Detects if the browser supports DOM Storage
164 | * or userData behavior and behaves accordingly.
165 | * @returns undefined
166 | */
167 | function _init(){
168 | /* Check if browser supports localStorage */
169 | var localStorageReallyWorks = false;
170 | if("localStorage" in window){
171 | try {
172 | window.localStorage.setItem('_tmptest', 'tmpval');
173 | localStorageReallyWorks = true;
174 | window.localStorage.removeItem('_tmptest');
175 | } catch(BogusQuotaExceededErrorOnIos5) {
176 | // Thanks be to iOS5 Private Browsing mode which throws
177 | // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
178 | }
179 | }
180 | if(localStorageReallyWorks){
181 | try {
182 | if(window.localStorage) {
183 | _storage_service = window.localStorage;
184 | _backend = "localStorage";
185 | }
186 | } catch(E3) {/* Firefox fails when touching localStorage and cookies are disabled */}
187 | }
188 | /* Check if browser supports globalStorage */
189 | else if("globalStorage" in window){
190 | try {
191 | if(window.globalStorage) {
192 | _storage_service = window.globalStorage[window.location.hostname];
193 | _backend = "globalStorage";
194 | }
195 | } catch(E4) {/* Firefox fails when touching localStorage and cookies are disabled */}
196 | }
197 | /* Check if browser supports userData behavior */
198 | else {
199 | _storage_elm = document.createElement('link');
200 | if(_storage_elm.addBehavior){
201 |
202 | /* Use a DOM element to act as userData storage */
203 | _storage_elm.style.behavior = 'url(#default#userData)';
204 |
205 | /* userData element needs to be inserted into the DOM! */
206 | document.getElementsByTagName('head')[0].appendChild(_storage_elm);
207 |
208 | _storage_elm.load("jStorage");
209 | var data = "{}";
210 | try{
211 | data = _storage_elm.getAttribute("jStorage");
212 | }catch(E5){}
213 | _storage_service.jStorage = data;
214 | _backend = "userDataBehavior";
215 | }else{
216 | _storage_elm = null;
217 | return;
218 | }
219 | }
220 |
221 | _load_storage();
222 |
223 | // remove dead keys
224 | _handleTTL();
225 | }
226 |
227 | /**
228 | * Loads the data from the storage based on the supported mechanism
229 | * @returns undefined
230 | */
231 | function _load_storage(){
232 | /* if jStorage string is retrieved, then decode it */
233 | if(_storage_service.jStorage){
234 | try{
235 | _storage = json_decode(String(_storage_service.jStorage));
236 | }catch(E6){_storage_service.jStorage = "{}";}
237 | }else{
238 | _storage_service.jStorage = "{}";
239 | }
240 | _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
241 | }
242 |
243 | /**
244 | * This functions provides the "save" mechanism to store the jStorage object
245 | * @returns undefined
246 | */
247 | function _save(){
248 | try{
249 | _storage_service.jStorage = json_encode(_storage);
250 | // If userData is used as the storage engine, additional
251 | if(_storage_elm) {
252 | _storage_elm.setAttribute("jStorage",_storage_service.jStorage);
253 | _storage_elm.save("jStorage");
254 | }
255 | _storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
256 | }catch(E7){/* probably cache is full, nothing is saved this way*/}
257 | }
258 |
259 | /**
260 | * Function checks if a key is set and is string or numberic
261 | */
262 | function _checkKey(key){
263 | if(!key || (typeof key != "string" && typeof key != "number")){
264 | throw new TypeError('Key name must be string or numeric');
265 | }
266 | if(key == "__jstorage_meta"){
267 | throw new TypeError('Reserved key name');
268 | }
269 | return true;
270 | }
271 |
272 | /**
273 | * Removes expired keys
274 | */
275 | function _handleTTL(){
276 | var curtime, i, TTL, nextExpire = Infinity, changed = false;
277 |
278 | clearTimeout(_ttl_timeout);
279 |
280 | if(!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object"){
281 | // nothing to do here
282 | return;
283 | }
284 |
285 | curtime = +new Date();
286 | TTL = _storage.__jstorage_meta.TTL;
287 | for(i in TTL){
288 | if(TTL.hasOwnProperty(i)){
289 | if(TTL[i] <= curtime){
290 | delete TTL[i];
291 | delete _storage[i];
292 | changed = true;
293 | }else if(TTL[i] < nextExpire){
294 | nextExpire = TTL[i];
295 | }
296 | }
297 | }
298 |
299 | // set next check
300 | if(nextExpire != Infinity){
301 | _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
302 | }
303 |
304 | // save changes
305 | if(changed){
306 | _save();
307 | }
308 | }
309 |
310 | ////////////////////////// PUBLIC INTERFACE /////////////////////////
311 |
312 | $.jStorage = {
313 | /* Version number */
314 | version: "0.1.6.1",
315 |
316 | /**
317 | * Sets a key's value.
318 | *
319 | * @param {String} key - Key to set. If this value is not set or not
320 | * a string an exception is raised.
321 | * @param value - Value to set. This can be any value that is JSON
322 | * compatible (Numbers, Strings, Objects etc.).
323 | * @returns the used value
324 | */
325 | set: function(key, value){
326 | _checkKey(key);
327 | if(_XMLService.isXML(value)){
328 | value = {_is_xml:true,xml:_XMLService.encode(value)};
329 | }else if(typeof value == "function"){
330 | value = null; // functions can't be saved!
331 | }else if(value && typeof value == "object"){
332 | // clone the object before saving to _storage tree
333 | value = json_decode(json_encode(value));
334 | }
335 | _storage[key] = value;
336 | _save();
337 | return value;
338 | },
339 |
340 | /**
341 | * Looks up a key in cache
342 | *
343 | * @param {String} key - Key to look up.
344 | * @param {mixed} def - Default value to return, if key didn't exist.
345 | * @returns the key value, default value or
346 | */
347 | get: function(key, def){
348 | _checkKey(key);
349 | if(key in _storage){
350 | if(_storage[key] && typeof _storage[key] == "object" &&
351 | _storage[key]._is_xml &&
352 | _storage[key]._is_xml){
353 | return _XMLService.decode(_storage[key].xml);
354 | }else{
355 | return _storage[key];
356 | }
357 | }
358 | return typeof(def) == 'undefined' ? null : def;
359 | },
360 |
361 | /**
362 | * Deletes a key from cache.
363 | *
364 | * @param {String} key - Key to delete.
365 | * @returns true if key existed or false if it didn't
366 | */
367 | deleteKey: function(key){
368 | _checkKey(key);
369 | if(key in _storage){
370 | delete _storage[key];
371 | // remove from TTL list
372 | if(_storage.__jstorage_meta &&
373 | typeof _storage.__jstorage_meta.TTL == "object" &&
374 | key in _storage.__jstorage_meta.TTL){
375 | delete _storage.__jstorage_meta.TTL[key];
376 | }
377 | _save();
378 | return true;
379 | }
380 | return false;
381 | },
382 |
383 | /**
384 | * Sets a TTL for a key, or remove it if ttl value is 0 or below
385 | *
386 | * @param {String} key - key to set the TTL for
387 | * @param {Number} ttl - TTL timeout in milliseconds
388 | * @returns true if key existed or false if it didn't
389 | */
390 | setTTL: function(key, ttl){
391 | var curtime = +new Date();
392 | _checkKey(key);
393 | ttl = Number(ttl) || 0;
394 | if(key in _storage){
395 |
396 | if(!_storage.__jstorage_meta){
397 | _storage.__jstorage_meta = {};
398 | }
399 | if(!_storage.__jstorage_meta.TTL){
400 | _storage.__jstorage_meta.TTL = {};
401 | }
402 |
403 | // Set TTL value for the key
404 | if(ttl>0){
405 | _storage.__jstorage_meta.TTL[key] = curtime + ttl;
406 | }else{
407 | delete _storage.__jstorage_meta.TTL[key];
408 | }
409 |
410 | _save();
411 |
412 | _handleTTL();
413 | return true;
414 | }
415 | return false;
416 | },
417 |
418 | /**
419 | * Deletes everything in cache.
420 | *
421 | * @return true
422 | */
423 | flush: function(){
424 | _storage = {};
425 | _save();
426 | return true;
427 | },
428 |
429 | /**
430 | * Returns a read-only copy of _storage
431 | *
432 | * @returns Object
433 | */
434 | storageObj: function(){
435 | function F() {}
436 | F.prototype = _storage;
437 | return new F();
438 | },
439 |
440 | /**
441 | * Returns an index of all used keys as an array
442 | * ['key1', 'key2',..'keyN']
443 | *
444 | * @returns Array
445 | */
446 | index: function(){
447 | var index = [], i;
448 | for(i in _storage){
449 | if(_storage.hasOwnProperty(i) && i != "__jstorage_meta"){
450 | index.push(i);
451 | }
452 | }
453 | return index;
454 | },
455 |
456 | /**
457 | * How much space in bytes does the storage take?
458 | *
459 | * @returns Number
460 | */
461 | storageSize: function(){
462 | return _storage_size;
463 | },
464 |
465 | /**
466 | * Which backend is currently in use?
467 | *
468 | * @returns String
469 | */
470 | currentBackend: function(){
471 | return _backend;
472 | },
473 |
474 | /**
475 | * Test if storage is available
476 | *
477 | * @returns Boolean
478 | */
479 | storageAvailable: function(){
480 | return !!_backend;
481 | },
482 |
483 | /**
484 | * Reloads the data from browser storage
485 | *
486 | * @returns undefined
487 | */
488 | reInit: function(){
489 | var new_storage_elm, data;
490 | if(_storage_elm && _storage_elm.addBehavior){
491 | new_storage_elm = document.createElement('link');
492 |
493 | _storage_elm.parentNode.replaceChild(new_storage_elm, _storage_elm);
494 | _storage_elm = new_storage_elm;
495 |
496 | /* Use a DOM element to act as userData storage */
497 | _storage_elm.style.behavior = 'url(#default#userData)';
498 |
499 | /* userData element needs to be inserted into the DOM! */
500 | document.getElementsByTagName('head')[0].appendChild(_storage_elm);
501 |
502 | _storage_elm.load("jStorage");
503 | data = "{}";
504 | try{
505 | data = _storage_elm.getAttribute("jStorage");
506 | }catch(E5){}
507 | _storage_service.jStorage = data;
508 | _backend = "userDataBehavior";
509 | }
510 |
511 | _load_storage();
512 | }
513 | };
514 |
515 | // Initialize jStorage
516 | _init();
517 |
518 | })(window.jQuery || window.$);
--------------------------------------------------------------------------------
/static/js/jstorage.min.js:
--------------------------------------------------------------------------------
1 | (function(g){function m(){if(e.jStorage)try{c=n(""+e.jStorage)}catch(a){e.jStorage="{}"}else e.jStorage="{}";j=e.jStorage?(""+e.jStorage).length:0}function h(){try{e.jStorage=o(c),d&&(d.setAttribute("jStorage",e.jStorage),d.save("jStorage")),j=e.jStorage?(""+e.jStorage).length:0}catch(a){}}function i(a){if(!a||"string"!=typeof a&&"number"!=typeof a)throw new TypeError("Key name must be string or numeric");if("__jstorage_meta"==a)throw new TypeError("Reserved key name");return!0}function k(){var a,
2 | b,d,e=Infinity,f=!1;clearTimeout(p);if(c.__jstorage_meta&&"object"==typeof c.__jstorage_meta.TTL){a=+new Date;d=c.__jstorage_meta.TTL;for(b in d)d.hasOwnProperty(b)&&(d[b]<=a?(delete d[b],delete c[b],f=!0):d[b]Loading...');
227 | $(link).remove();
228 | container.append(output);
229 | $.ajax('/blame/web/' + file + '@' + line + ',' + line, {
230 | dataType: 'jsonp'
231 | , success: function (data, textStatus, jqXHR) {
232 | if (data.error) {
233 |
234 | }
235 | output.text(data.error ?
236 | ('[Blamebot error: ' + (data.error.details || data.error.msg) + ']')
237 | :
238 | ('[' + (new Date(data['author-time'] * 1000)).format('m/d HH:MM:ss') + '] ' + data['author'] + ' - ' + data['summary'])
239 | );
240 | }
241 | });
242 | };
243 |
244 | });
245 |
--------------------------------------------------------------------------------
/static/js/page.min.js:
--------------------------------------------------------------------------------
1 | //This is for the benefit of jStorage
2 | jQuery.extend({toJSON:JSON.stringify,evalJSON:JSON.parse}),jQuery(function(a){function d(){if(c)return;var d={};a(".display-option input, .display-option textarea").each(function(a,b){d[b.id]=b.type==="checkbox"?b.checked:b.value}),b=encodeURIComponent(JSON.stringify(d)),window.location.hash=b}function e(){var b=null;try{b=JSON.parse(decodeURIComponent(window.location.hash.replace(/^#/,"")))}catch(c){return}a(".display-option input, .display-option textarea").each(function(c,d){if(d.id in b){var e=null;d.type==="checkbox"?(e=d.checked,d.checked=b[d.id]):(e=d.value,d.value=b[d.id]),e!==b[d.id]&&a(d).trigger("change")}})}window.Etsy=window.Etsy||{},window.Etsy.Page=window.Etsy.Page||{};var b=window.location.hash;(function(){"onhashchange"in window||setInterval(function(){window.location.hash!==b&&(b=window.location.hash,a(window).trigger("hashchange"))},100)})();var c=!0;a(window).bind("hashchange",function(){b!==window.location.hash&&e()}),e(),a(window).bind("blur",function(){Etsy.Supergrep.setBackground(!0)}),a(window).bind("focus",function(){Etsy.Supergrep.setBackground(!1)}).trigger("focus"),a("#clear-log").bind("click",function(){return Etsy.Supergrep.clearEntries(),!1}),a("#legend-help").bind("click",function(){return a("#legend-dialog").dialog({autoOpen:!0,width:600,buttons:{Ok:function(){a(this).dialog("close")}}}),!1}),a("#guide-help").bind("click",function(){return guiders.hideAll(),guiders.show("guide_welcome"),!1}),a(".rule-help").bind("click",function(){return a("#rule-help-dialog").dialog({autoOpen:!0,width:600,buttons:{Ok:function(){a(this).dialog("close")}}}),!1}),a("#wrap-lines").bind("change",function(){d(),a("#results").toggleClass("wrapping",this.checked)}).trigger("change"),a("#sort-order").bind("change",function(){d(),Etsy.Supergrep.setSortOrder(this.checked?Etsy.Supergrep.SortOrder.Ascending:Etsy.Supergrep.SortOrder.Descending)}).trigger("change"),a("#autoscroll").bind("change",function(){d(),Etsy.Supergrep.setAutoscroll(this.checked)}).trigger("change"),a("#max-entries").bind("change",function(){var a=parseInt(this.value,10);if(isNaN(a)){this.value=Etsy.Supergrep.getMaxEntries();return}this.value=a,d(),Etsy.Supergrep.setMaxEntries(a)}).trigger("change"),a("#clear-field-filter").bind("click",function(){return a("#filter-log").val("").trigger("change"),!1}),a("#filter-log").bind("change",function(){var b=!1;try{b=!!Etsy.Supergrep.parseFilterRule(this.value)}catch(c){}if(a.trim(this.value)!==""&&!b){a("#filter-log").addClass("error");return}a("#filter-log").removeClass("error"),d(),Etsy.Supergrep.setFilter(this.value)}).trigger("change"),a("#filter-invert-option").bind("change",function(){Etsy.Supergrep.setFilterInvert(!!a("#filter-invert-option:checked").length)}).trigger("change"),a("#clear-field-highlight").bind("click",function(){return a("#highlight-log").val("").trigger("change"),!1}),a("#highlight-log").bind("change",function(){var b=!1;try{b=!!Etsy.Supergrep.parseFilterRule(this.value)}catch(c){}if(a.trim(this.value)!==""&&!b){a("#highlight-log").addClass("error");return}a("#highlight-log").removeClass("error"),d(),Etsy.Supergrep.setHighlight(this.value)}).trigger("change"),a(".log-option").bind("change",function(){var b=[],c=[];a(".log-option").each(function(){var d=a(this).attr("data-log");a(this).is(":checked")?b.push(d):c.push(d)}),d(),Etsy.Supergrep.watchLogs(b,c)}),a("#log-web").trigger("change"),c=!1,window.Etsy.Page.showBlame=function(c,d,e){var f=a(c).parents("li:first"),g=a('Loading...');a(c).remove(),f.append(g),a.ajax("/blame/web/"+d+"@"+e+","+e,{dataType:"jsonp",success:function(a,b,c){!a.error,g.text(a.error?"[Blamebot error: "+(a.error.details||a.error.msg)+"]":"["+(new Date(a["author-time"]*1e3)).format("m/d HH:MM:ss")+"] "+a.author+" - "+a.summary)}})}});
--------------------------------------------------------------------------------
/static/js/sha-256.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined
3 | * in FIPS 180-2
4 | * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
6 | * Distributed under the BSD License
7 | * See http://pajhome.org.uk/crypt/md5 for details.
8 | * Also http://anmar.eu.org/projects/jssha2/
9 | */
10 |
11 | /*
12 | * Configurable variables. You may need to tweak these to be compatible with
13 | * the server-side, but the defaults work in most cases.
14 | */
15 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
16 | var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
17 |
18 | /*
19 | * These are the functions you'll usually want to call
20 | * They take string arguments and return either hex or base-64 encoded strings
21 | */
22 | function hex_sha256(s) { return rstr2hex(rstr_sha256(str2rstr_utf8(s))); }
23 | function b64_sha256(s) { return rstr2b64(rstr_sha256(str2rstr_utf8(s))); }
24 | function any_sha256(s, e) { return rstr2any(rstr_sha256(str2rstr_utf8(s)), e); }
25 | function hex_hmac_sha256(k, d)
26 | { return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); }
27 | function b64_hmac_sha256(k, d)
28 | { return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); }
29 | function any_hmac_sha256(k, d, e)
30 | { return rstr2any(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
31 |
32 | /*
33 | * Perform a simple self-test to see if the VM is working
34 | */
35 | function sha256_vm_test()
36 | {
37 | return hex_sha256("abc").toLowerCase() ==
38 | "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
39 | }
40 |
41 | /*
42 | * Calculate the sha256 of a raw string
43 | */
44 | function rstr_sha256(s)
45 | {
46 | return binb2rstr(binb_sha256(rstr2binb(s), s.length * 8));
47 | }
48 |
49 | /*
50 | * Calculate the HMAC-sha256 of a key and some data (raw strings)
51 | */
52 | function rstr_hmac_sha256(key, data)
53 | {
54 | var bkey = rstr2binb(key);
55 | if(bkey.length > 16) bkey = binb_sha256(bkey, key.length * 8);
56 |
57 | var ipad = Array(16), opad = Array(16);
58 | for(var i = 0; i < 16; i++)
59 | {
60 | ipad[i] = bkey[i] ^ 0x36363636;
61 | opad[i] = bkey[i] ^ 0x5C5C5C5C;
62 | }
63 |
64 | var hash = binb_sha256(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
65 | return binb2rstr(binb_sha256(opad.concat(hash), 512 + 256));
66 | }
67 |
68 | /*
69 | * Convert a raw string to a hex string
70 | */
71 | function rstr2hex(input)
72 | {
73 | try { hexcase } catch(e) { hexcase=0; }
74 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
75 | var output = "";
76 | var x;
77 | for(var i = 0; i < input.length; i++)
78 | {
79 | x = input.charCodeAt(i);
80 | output += hex_tab.charAt((x >>> 4) & 0x0F)
81 | + hex_tab.charAt( x & 0x0F);
82 | }
83 | return output;
84 | }
85 |
86 | /*
87 | * Convert a raw string to a base-64 string
88 | */
89 | function rstr2b64(input)
90 | {
91 | try { b64pad } catch(e) { b64pad=''; }
92 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
93 | var output = "";
94 | var len = input.length;
95 | for(var i = 0; i < len; i += 3)
96 | {
97 | var triplet = (input.charCodeAt(i) << 16)
98 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
99 | | (i + 2 < len ? input.charCodeAt(i+2) : 0);
100 | for(var j = 0; j < 4; j++)
101 | {
102 | if(i * 8 + j * 6 > input.length * 8) output += b64pad;
103 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
104 | }
105 | }
106 | return output;
107 | }
108 |
109 | /*
110 | * Convert a raw string to an arbitrary string encoding
111 | */
112 | function rstr2any(input, encoding)
113 | {
114 | var divisor = encoding.length;
115 | var remainders = Array();
116 | var i, q, x, quotient;
117 |
118 | /* Convert to an array of 16-bit big-endian values, forming the dividend */
119 | var dividend = Array(Math.ceil(input.length / 2));
120 | for(i = 0; i < dividend.length; i++)
121 | {
122 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
123 | }
124 |
125 | /*
126 | * Repeatedly perform a long division. The binary array forms the dividend,
127 | * the length of the encoding is the divisor. Once computed, the quotient
128 | * forms the dividend for the next step. We stop when the dividend is zero.
129 | * All remainders are stored for later use.
130 | */
131 | while(dividend.length > 0)
132 | {
133 | quotient = Array();
134 | x = 0;
135 | for(i = 0; i < dividend.length; i++)
136 | {
137 | x = (x << 16) + dividend[i];
138 | q = Math.floor(x / divisor);
139 | x -= q * divisor;
140 | if(quotient.length > 0 || q > 0)
141 | quotient[quotient.length] = q;
142 | }
143 | remainders[remainders.length] = x;
144 | dividend = quotient;
145 | }
146 |
147 | /* Convert the remainders to the output string */
148 | var output = "";
149 | for(i = remainders.length - 1; i >= 0; i--)
150 | output += encoding.charAt(remainders[i]);
151 |
152 | /* Append leading zero equivalents */
153 | var full_length = Math.ceil(input.length * 8 /
154 | (Math.log(encoding.length) / Math.log(2)))
155 | for(i = output.length; i < full_length; i++)
156 | output = encoding[0] + output;
157 |
158 | return output;
159 | }
160 |
161 | /*
162 | * Encode a string as utf-8.
163 | * For efficiency, this assumes the input is valid utf-16.
164 | */
165 | function str2rstr_utf8(input)
166 | {
167 | var output = "";
168 | var i = -1;
169 | var x, y;
170 |
171 | while(++i < input.length)
172 | {
173 | /* Decode utf-16 surrogate pairs */
174 | x = input.charCodeAt(i);
175 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
176 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
177 | {
178 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
179 | i++;
180 | }
181 |
182 | /* Encode output as utf-8 */
183 | if(x <= 0x7F)
184 | output += String.fromCharCode(x);
185 | else if(x <= 0x7FF)
186 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
187 | 0x80 | ( x & 0x3F));
188 | else if(x <= 0xFFFF)
189 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
190 | 0x80 | ((x >>> 6 ) & 0x3F),
191 | 0x80 | ( x & 0x3F));
192 | else if(x <= 0x1FFFFF)
193 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
194 | 0x80 | ((x >>> 12) & 0x3F),
195 | 0x80 | ((x >>> 6 ) & 0x3F),
196 | 0x80 | ( x & 0x3F));
197 | }
198 | return output;
199 | }
200 |
201 | /*
202 | * Encode a string as utf-16
203 | */
204 | function str2rstr_utf16le(input)
205 | {
206 | var output = "";
207 | for(var i = 0; i < input.length; i++)
208 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
209 | (input.charCodeAt(i) >>> 8) & 0xFF);
210 | return output;
211 | }
212 |
213 | function str2rstr_utf16be(input)
214 | {
215 | var output = "";
216 | for(var i = 0; i < input.length; i++)
217 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
218 | input.charCodeAt(i) & 0xFF);
219 | return output;
220 | }
221 |
222 | /*
223 | * Convert a raw string to an array of big-endian words
224 | * Characters >255 have their high-byte silently ignored.
225 | */
226 | function rstr2binb(input)
227 | {
228 | var output = Array(input.length >> 2);
229 | for(var i = 0; i < output.length; i++)
230 | output[i] = 0;
231 | for(var i = 0; i < input.length * 8; i += 8)
232 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
233 | return output;
234 | }
235 |
236 | /*
237 | * Convert an array of big-endian words to a string
238 | */
239 | function binb2rstr(input)
240 | {
241 | var output = "";
242 | for(var i = 0; i < input.length * 32; i += 8)
243 | output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
244 | return output;
245 | }
246 |
247 | /*
248 | * Main sha256 function, with its support functions
249 | */
250 | function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
251 | function sha256_R (X, n) {return ( X >>> n );}
252 | function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
253 | function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
254 | function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
255 | function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
256 | function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
257 | function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
258 | function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
259 | function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
260 | function sha256_Gamma0512(x) {return (sha256_S(x, 1) ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
261 | function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
262 |
263 | var sha256_K = new Array
264 | (
265 | 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
266 | -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
267 | 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
268 | 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
269 | -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
270 | 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
271 | 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
272 | -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
273 | 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
274 | 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
275 | -1866530822, -1538233109, -1090935817, -965641998
276 | );
277 |
278 | function binb_sha256(m, l)
279 | {
280 | var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,
281 | 1359893119, -1694144372, 528734635, 1541459225);
282 | var W = new Array(64);
283 | var a, b, c, d, e, f, g, h;
284 | var i, j, T1, T2;
285 |
286 | /* append padding */
287 | m[l >> 5] |= 0x80 << (24 - l % 32);
288 | m[((l + 64 >> 9) << 4) + 15] = l;
289 |
290 | for(i = 0; i < m.length; i += 16)
291 | {
292 | a = HASH[0];
293 | b = HASH[1];
294 | c = HASH[2];
295 | d = HASH[3];
296 | e = HASH[4];
297 | f = HASH[5];
298 | g = HASH[6];
299 | h = HASH[7];
300 |
301 | for(j = 0; j < 64; j++)
302 | {
303 | if (j < 16) W[j] = m[j + i];
304 | else W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
305 | sha256_Gamma0256(W[j - 15])), W[j - 16]);
306 |
307 | T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
308 | sha256_K[j]), W[j]);
309 | T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
310 | h = g;
311 | g = f;
312 | f = e;
313 | e = safe_add(d, T1);
314 | d = c;
315 | c = b;
316 | b = a;
317 | a = safe_add(T1, T2);
318 | }
319 |
320 | HASH[0] = safe_add(a, HASH[0]);
321 | HASH[1] = safe_add(b, HASH[1]);
322 | HASH[2] = safe_add(c, HASH[2]);
323 | HASH[3] = safe_add(d, HASH[3]);
324 | HASH[4] = safe_add(e, HASH[4]);
325 | HASH[5] = safe_add(f, HASH[5]);
326 | HASH[6] = safe_add(g, HASH[6]);
327 | HASH[7] = safe_add(h, HASH[7]);
328 | }
329 | return HASH;
330 | }
331 |
332 | function safe_add (x, y)
333 | {
334 | var lsw = (x & 0xFFFF) + (y & 0xFFFF);
335 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
336 | return (msw << 16) | (lsw & 0xFFFF);
337 | }
338 |
--------------------------------------------------------------------------------
/static/js/sha-256.min.js:
--------------------------------------------------------------------------------
1 | var hexcase=0,b64pad="";function hex_sha256(a){return rstr2hex(rstr_sha256(str2rstr_utf8(a)))}function b64_sha256(a){return rstr2b64(rstr_sha256(str2rstr_utf8(a)))}function any_sha256(a,c){return rstr2any(rstr_sha256(str2rstr_utf8(a)),c)}function hex_hmac_sha256(a,c){return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(a),str2rstr_utf8(c)))}function b64_hmac_sha256(a,c){return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(a),str2rstr_utf8(c)))} function any_hmac_sha256(a,c,b){return rstr2any(rstr_hmac_sha256(str2rstr_utf8(a),str2rstr_utf8(c)),b)}function sha256_vm_test(){return"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"==hex_sha256("abc").toLowerCase()}function rstr_sha256(a){return binb2rstr(binb_sha256(rstr2binb(a),8*a.length))} function rstr_hmac_sha256(a,c){var b=rstr2binb(a);16f;f++)d[f]=b[f]^909522486,e[f]=b[f]^1549556828;b=binb_sha256(d.concat(rstr2binb(c)),512+8*c.length);return binb2rstr(binb_sha256(e.concat(b),768))}function rstr2hex(a){for(var c=hexcase?"0123456789ABCDEF":"0123456789abcdef",b="",d,e=0;e>>4&15)+c.charAt(d&15);return b} function rstr2b64(a){for(var c="",b=a.length,d=0;df;f++)c=8*d+6*f>8*a.length?c+b64pad:c+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(e>>>6*(3-f)&63);return c} function rstr2any(a,c){var b=c.length,d=[],e,f,g,j,h=Array(Math.ceil(a.length/2));for(e=0;e=d&&56320<=e&&57343>=e&&(d=65536+((d&1023)<<10)+(e&1023),b++),127>=d?c+=String.fromCharCode(d):2047>=d?c+=String.fromCharCode(192|d>>>6&31,128|d&63):65535>=d?c+=String.fromCharCode(224|d>>>12&15,128|d>>>6&63,128|d&63):2097151>=d&&(c+=String.fromCharCode(240|d>>>18&7,128|d>>>12&63,128|d>>>6&63,128|d&63));return c} function str2rstr_utf16le(a){for(var c="",b=0;b>>8&255);return c}function str2rstr_utf16be(a){for(var c="",b=0;b>>8&255,a.charCodeAt(b)&255);return c}function rstr2binb(a){for(var c=Array(a.length>>2),b=0;b>5]|=(a.charCodeAt(b/8)&255)<<24-b%32;return c} function binb2rstr(a){for(var c="",b=0;b<32*a.length;b+=8)c+=String.fromCharCode(a[b>>5]>>>24-b%32&255);return c}function sha256_S(a,c){return a>>>c|a<<32-c}function sha256_R(a,c){return a>>>c}function sha256_Ch(a,c,b){return a&c^~a&b}function sha256_Maj(a,c,b){return a&c^a&b^c&b}function sha256_Sigma0256(a){return sha256_S(a,2)^sha256_S(a,13)^sha256_S(a,22)}function sha256_Sigma1256(a){return sha256_S(a,6)^sha256_S(a,11)^sha256_S(a,25)} function sha256_Gamma0256(a){return sha256_S(a,7)^sha256_S(a,18)^sha256_R(a,3)}function sha256_Gamma1256(a){return sha256_S(a,17)^sha256_S(a,19)^sha256_R(a,10)}function sha256_Sigma0512(a){return sha256_S(a,28)^sha256_S(a,34)^sha256_S(a,39)}function sha256_Sigma1512(a){return sha256_S(a,14)^sha256_S(a,18)^sha256_S(a,41)}function sha256_Gamma0512(a){return sha256_S(a,1)^sha256_S(a,8)^sha256_R(a,7)}function sha256_Gamma1512(a){return sha256_S(a,19)^sha256_S(a,61)^sha256_R(a,6)} var sha256_K=[1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525, -778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998]; function binb_sha256(a,c){var b=[1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225],d=Array(64),e,f,g,j,h,k,l,n,m,i,o,p;a[c>>5]|=128<<24-c%32;a[(c+64>>9<<4)+15]=c;for(m=0;mi;i++)d[i]=16>i?a[i+m]:safe_add(safe_add(safe_add(sha256_Gamma1256(d[i-2]),d[i-7]),sha256_Gamma0256(d[i-15])),d[i-16]),o=safe_add(safe_add(safe_add(safe_add(n,sha256_Sigma1256(h)),sha256_Ch(h,k,l)),sha256_K[i]), d[i]),p=safe_add(sha256_Sigma0256(e),sha256_Maj(e,f,g)),n=l,l=k,k=h,h=safe_add(j,o),j=g,g=f,f=e,e=safe_add(o,p);b[0]=safe_add(e,b[0]);b[1]=safe_add(f,b[1]);b[2]=safe_add(g,b[2]);b[3]=safe_add(j,b[3]);b[4]=safe_add(h,b[4]);b[5]=safe_add(k,b[5]);b[6]=safe_add(l,b[6]);b[7]=safe_add(n,b[7])}return b}function safe_add(a,c){var b=(a&65535)+(c&65535);return(a>>16)+(c>>16)+(b>>16)<<16|b&65535};
--------------------------------------------------------------------------------
/static/js/supergrep.js:
--------------------------------------------------------------------------------
1 | // Log streaming
2 | (function (window, $) {
3 | window.console = window.console || { log: function () {} };
4 |
5 | window.Etsy = window.Etsy || {};
6 | window.Etsy.Supergrep = window.Etsy.Supergrep || {};
7 |
8 | var sg = window.Etsy.Supergrep;
9 |
10 | sg.SortOrder = {
11 | Ascending: 'asc',
12 | Descending: 'dsc'
13 | };
14 |
15 | var pvt = {
16 | socket: null,
17 | targetLogs: [],
18 | logEntries: [],
19 | entryCounter: 0,
20 | hashes: {},
21 | unseen: 0,
22 | config: {
23 | maxEntries: 500,
24 | sortOrder: sg.SortOrder.Descending,
25 | autoscroll: true,
26 | filter: null,
27 | highlight: null,
28 | background: false,
29 | pageTitle: 'Supergrep++'
30 | },
31 | paths: {
32 | githubWebPrefix: 'https://github.com/your/repo/blob/master/',
33 | },
34 | regex: {
35 | /* Log parsing regexes */
36 | meta: /^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s\[([^\]]+)\]\s\[(?:client\s*)?([^\]]+)\]/i,
37 | //DEV version of META regex
38 | meta_dev: /^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s\[([^\]]+)\]\s\[(?:client\s*)?([^\]]+)\]/i,
39 |
40 | web: /^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s(?:\[[^\]]+\])\s\[(?:client\s*)?([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s+/i,
41 | //DEV version of WEB regex
42 | web_dev: /^()\[([^\\[\]]+)\]\s()\[([^\\[\]]+)\]\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+/i,
43 |
44 | gearman: /^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s()\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s+/i,
45 | //DEV version of GEARMAN regex
46 | gearman_dev: /^()\[([^\]]+)\]\s()\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s+/i,
47 |
48 | /* Log data parsing regexes */
49 | error: /\[(error)\]/ig,
50 | warning: /\[(warning)\]/ig,
51 | info: /\[((info|notice))\]/ig,
52 | debug: /\[(debug)\]/ig,
53 | phpFatal: /PHP Fatal/i,
54 |
55 | /* Stacktrace parsing regexes */
56 | stacktrace: /(\s#\d+\s)/g,
57 | fullStacktrace: /(?:stack trace:\s+|backtrace:(?:\\n|\s)*)(#\d+.+\.php(?:\:\d+|\(\d+\)))/i,
58 | stackSplitter: /\s*#\d+\s+/,
59 | stackFile: /(\/your\/path\/here\/|\/home\/[^\\\/]+)/i,
60 | phpFile: /\.php/i,
61 | stackInternalFunc: /\[internal\sfunction\]:\s*/i,
62 | stackFileLine: /\:\d+$/,
63 | stackAltFileLine: /\.php\((\d+)\):/i,
64 |
65 | /* Misc */
66 | codepath: /((\/your\/path\/here\/)([^\.]+.php)(?:\(([\d]+)\)|(?:\s*on)?\s+line\s+(\d+)|:(\d+)))/i,
67 | //Check to see if log is from DEV server
68 | devtest: /^\[/,
69 | token: /\[([a-z0-9-_]{28})\]/i,
70 | severity: /(?:\[[a-z0-9-_]{28}\])\s\[(error|warning)\]/i,
71 | fileNotFound: /File does not exist/i,
72 | url: /((?:https?:\/\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig
73 | }
74 | };
75 |
76 | function socketReconnect(sleep) {
77 | sg.writeMsg("Lost connection... retrying in " + sleep + " seconds");
78 | //TODO: configure timeout
79 | setTimeout(function() {
80 | if (!pvt.socket.socket.connected) {
81 | pvt.socket = io.connect();
82 | }
83 | }, sleep * 1000);
84 | setTimeout(function() {
85 | if (!pvt.socket.socket.connected && !pvt.socket.socket.connecting) {
86 | socketReconnect(sleep * 2);
87 | }
88 | }, (sleep + 3) * 1000);
89 | }
90 |
91 | sg.submitGist = function Etsy$Supergrep$submitGist (ns, timestamp, data) {
92 | $('#gistform-filename').val("supergrep[" + ns + "]" + timestamp);
93 | $('#gistform-data').val(unescape(data));
94 | $('#gistform').get(0).submit();
95 | };
96 |
97 | sg.submitIrc = function Etsy$Supergrep$submitIrc (target, data) {
98 | target = $.trim(target);
99 |
100 | if (!target) {
101 | return;
102 | }
103 | if (!target.match(/^(@|#)/)) {
104 | return;
105 | }
106 |
107 | var targetParts = target.split(' ');
108 | target = targetParts.shift();
109 | data = targetParts.join(' ') + "\n" + data;
110 |
111 | $.ajax('/v2/irccat', {
112 | type: 'POST',
113 | data: { target: target, data: data }
114 | });
115 | };
116 |
117 | sg.watchLogs = function Etsy$Supergrep$watchLogs (subscribedLogs, unsubscribedLogs) {
118 | pvt.subscribedLogs = subscribedLogs;
119 | pvt.unsubscribedLogs = unsubscribedLogs;
120 |
121 | if (!pvt.socket) {
122 | pvt.socket = io.connect(document.location.hostname, {
123 | transports: ['xhr-polling']
124 | });
125 |
126 | pvt.socket.on('connect', function () {
127 | $("#loading").hide();
128 | sg.writeMsg("Connection established");
129 | pvt.socket.emit('subscriptions', {
130 | subscribeTo: pvt.subscribedLogs,
131 | unsubscribeFrom: pvt.unsubscribedLogs
132 | });
133 | });
134 |
135 | pvt.socket.on('lines', function (data) {
136 | sg.writeLogEntries(data);
137 | });
138 |
139 | pvt.socket.on('disconnect', function () {
140 | if (! pvt.socket.connected) {
141 | socketReconnect(2);
142 | }
143 | });
144 | } else {
145 | pvt.socket.emit('subscriptions', {
146 | subscribeTo: pvt.subscribedLogs,
147 | unsubscribeFrom: pvt.unsubscribedLogs
148 | });
149 | }
150 | };
151 |
152 | sg.setBackground = function Etsy$Supergrep$setBackground (isBackground) {
153 | pvt.config.background = !!isBackground;
154 | if (!pvt.config.background) {
155 | pvt.unseen = 0;
156 | $(document).attr("title", pvt.config.pageTitle);
157 | }
158 | };
159 |
160 | sg.parseFilterRule = function Etsy$Supergrep$parseFilterRule (rule) {
161 | rule = $.trim(rule);
162 | if (rule === '') {
163 | return null;
164 | }
165 |
166 | var result = rule.match(/^\/(.+)\/([igsm]*)$/);
167 | if (result) {
168 | return new RegExp(result[1], result[2]);
169 | }
170 |
171 | rule = rule.replace(/([\\\/\.\*\+\?\(\)\[\]\{\}\-\|])/g, '\\$1');
172 | var ruleParts = rule.split(/(?:\r\n|\n|\,)+/);
173 | var rulePartsSanitized = [];
174 | for (var i = 0, len = ruleParts.length; i < len; i++) {
175 | var part = $.trim(ruleParts[i]);
176 | if (part !== '') {
177 | rulePartsSanitized.push(part);
178 | }
179 | }
180 | if (!rulePartsSanitized.length) {
181 | return null;
182 | }
183 |
184 | return new RegExp('(' + rulePartsSanitized.join('|') + ')', 'i');
185 | };
186 |
187 | sg.setFilterInvert = function Etsy$Supergrep$setFilterInvert (invert) {
188 | pvt.config.filterInvert = !!invert;
189 | for (var i = 0, len = pvt.logEntries.length; i < len; i++) {
190 | this.evalLogEntry(pvt.logEntries[i]);
191 | }
192 | };
193 |
194 | sg.setFilter = function Etsy$Supergrep$setFilter (match) {
195 | pvt.config.filter = sg.parseFilterRule(match);
196 | for (var i = 0, len = pvt.logEntries.length; i < len; i++) {
197 | this.evalLogEntry(pvt.logEntries[i]);
198 | }
199 | };
200 |
201 | sg.setHighlight = function Etsy$Supergrep$setHighlight (match) {
202 | pvt.config.highlight = sg.parseFilterRule(match);
203 | for (var i = 0, len = pvt.logEntries.length; i < len; i++) {
204 | this.evalLogEntry(pvt.logEntries[i]);
205 | }
206 | };
207 |
208 | sg.evalLogEntry = function Etsy$Supergrep$evalLogEntry (entry) {
209 | if (pvt.config.highlight && entry.extra.rawdata.match(pvt.config.highlight)) {
210 | entry.element.addClass('highlight');
211 | } else {
212 | entry.element.removeClass('highlight');
213 | }
214 | if (pvt.config.filter && entry.extra.rawdata.match(pvt.config.filter)) {
215 | if (pvt.config.filterInvert) {
216 | entry.element.removeClass('hide');
217 | return true;
218 | } else {
219 | entry.element.addClass('hide');
220 | return false;
221 | }
222 | } else {
223 | if (pvt.config.filter && pvt.config.filterInvert) {
224 | entry.element.addClass('hide');
225 | return false;
226 | } else {
227 | entry.element.removeClass('hide');
228 | return true;
229 | }
230 | }
231 | };
232 |
233 | sg.setAutoscroll = function Etsy$Supergrep$setAutoscroll (autoscroll) {
234 | pvt.config.autoscroll = !!autoscroll;
235 | sg.scrollLog();
236 | };
237 |
238 | sg.scrollLog = function Etsy$Supergrep$scrollLog (entry) {
239 | if (pvt.config.sortOrder === this.SortOrder.Ascending) {
240 | if (!pvt.config.autoscroll) {
241 | return;
242 | }
243 | $("html").scrollTop($("html").height());
244 | } else {
245 | if (!pvt.config.autoscroll && entry) {
246 | $("html").scrollTop($("html").scrollTop() + entry.outerHeight());
247 | } else {
248 | $("html").scrollTop(0);
249 | }
250 | }
251 | };
252 |
253 |
254 | sg.setSortOrder = function Etsy$Supergrep$setSortOrder (sortOrder) {
255 | if (pvt.config.sortOrder === sortOrder) {
256 | return;
257 | }
258 | pvt.config.sortOrder = sortOrder;
259 | $('#results-list').empty();
260 | for (var i = 0, len = pvt.logEntries.length; i < len; i++) {
261 | this.displayEntry(pvt.logEntries[i].element);
262 | }
263 | };
264 |
265 | sg.getMaxEntries = function Etsy$Supergrep$getMaxEntries () {
266 | return pvt.config.maxEntries;
267 | };
268 |
269 | sg.setMaxEntries = function Etsy$Supergrep$setMaxEntries (max) {
270 | if (pvt.config.maxEntries === max) {
271 | return;
272 | }
273 | pvt.config.maxEntries = max;
274 | sg.trimEntries();
275 | };
276 |
277 | sg.trimEntries = function Etsy$Supergrep$trimEntries () {
278 | while (pvt.logEntries.length > pvt.config.maxEntries) {
279 | var entry = pvt.logEntries.shift();
280 | entry.element.remove();
281 | if (entry.extra.hash) {
282 | delete pvt.hashes[entry.extra.hash];
283 | }
284 | }
285 | };
286 |
287 | sg.displayEntry = function Etsy$Supergrep$displayEntry (element) {
288 | if (pvt.config.sortOrder === this.SortOrder.Ascending) {
289 | $(element).appendTo('#results-list');
290 | } else {
291 | $(element).prependTo('#results-list');
292 | }
293 | sg.scrollLog($(element));
294 | };
295 |
296 | sg.addEntry = function Etsy$Supergrep$addEntry (element, extra) {
297 | element = $(element);
298 | var entry = { element: element, extra: extra };
299 | var showing_entry = sg.evalLogEntry(entry);
300 | pvt.logEntries.push(entry);
301 | this.displayEntry(element);
302 | this.trimEntries();
303 | if (showing_entry && pvt.config.background) {
304 | pvt.unseen++;
305 | $(document).attr("title", '(' + pvt.unseen + ' new) ' + pvt.config.pageTitle);
306 | }
307 | };
308 |
309 | sg.clearEntries = function Etsy$Supergrep$clearEntries () {
310 | pvt.logEntries = [];
311 | $('#results-list').empty();
312 | };
313 |
314 | sg.writeMsg = function Etsy$Supergrep$writeMsg (data) {
315 | var templateData = { id: 'msg' + pvt.entryCounter++, data: data, rawdata: data };
316 | this.addEntry($(CWinberry.Templating.render('tpl_msgEntry', templateData)), templateData);
317 | };
318 |
319 | sg.parseLogEntry = function Etsy$Supergrep$parseLogEntry (ns, data) {
320 | try{
321 | var output = { ns: ns, rawdata: data, id: 'log' + pvt.entryCounter++ };
322 |
323 | //Check if the logs are from a DEV server
324 | var isDev = pvt.regex.devtest.test(data);
325 |
326 | var meta_matches = data.match(pvt.regex.meta);
327 | var error_matches = data.match(pvt.regex.error);
328 | var warning_matches = data.match(pvt.regex.warning);
329 | var token_matches = data.match(pvt.regex.token);
330 | var severity_matches = data.match(pvt.regex.severity);
331 |
332 | var severity = severity_matches ? severity_matches[1] : 'info';
333 |
334 | //TODO: break parsing into multiple functions
335 | if (ns === 'web') {
336 | var web_matches = data.match(
337 | (ns === 'gearman') ?
338 | (isDev ? pvt.regex.gearman_dev : pvt.regex.gearman)
339 | :
340 | (isDev ? pvt.regex.web_dev : pvt.regex.web)
341 | );
342 | if (web_matches && web_matches.length === 9) {
343 | data = data.replace(web_matches[0], '');
344 | output.server = web_matches[1] || 'localhost';
345 | output.timestamp = new Date(web_matches[2]);
346 | //TODO: get better data parsing
347 | if (isNaN(output.timestamp.getTime())) {
348 | output.timestamp = new Date(0);
349 | }
350 | output.client = web_matches[3] || '127.0.0.1';
351 | output.hash = web_matches[4];
352 | output.severity = web_matches[5];
353 | output.namespace = web_matches[6];
354 | //TODO: standardize this
355 | var pathTmp = web_matches[7] ? web_matches[7].split(':') : ['', 0];
356 | output.path = {
357 | url: web_matches[7].replace(pvt.regex.codepath, pvt.paths.githubWebPrefix + '$3#L$4$5$6'),
358 | file: pathTmp[0].replace(pvt.regex.stackFile, ''),
359 | line: pathTmp[1]
360 | };
361 | output.userid = (web_matches[8] && web_matches[8] !== '0') ? web_matches[8] : '';
362 |
363 | // Parse the callstack
364 | var fullStack = data.match(pvt.regex.fullStacktrace);
365 | if (fullStack) {
366 | output.stacktrace = [];
367 | var stack = fullStack[1].split(pvt.regex.stackSplitter);
368 | stack.shift();
369 | for (var i = 0, len = stack.length; i < len; i++) {
370 | var stackParts = stack[i].replace(pvt.regex.stackInternalFunc, '').split(' ');
371 | var codeLine;
372 | if (stack[i].match(pvt.regex.phpFile)) {
373 | codeLine = stack[i].match(pvt.regex.stackAltFileLine) ?
374 | stackParts.shift().replace(pvt.regex.stackAltFileLine, '.php:$1')
375 | :
376 | stackParts.pop()
377 | ;
378 | } else {
379 | codeLine = '';
380 | }
381 | var codeLineParts;
382 | if (codeLine.match(pvt.regex.stackFileLine)) {
383 | codeLineParts = codeLine.split(':');
384 | } else {
385 | codeLineParts = ['', '0'];
386 | stackParts.push(codeLine);
387 | }
388 | var stackCall = stackParts.join(' ');
389 | var file = codeLineParts[0].replace(pvt.regex.stackFile, '');
390 | var line = codeLineParts[1].replace('):', '');
391 | if (file.indexOf('/') === 0) {
392 | file = file.substring(1);
393 | }
394 | output.stacktrace.push({
395 | call: stackCall,
396 | file: file,
397 | line: line,
398 | url: !file ? '' : (pvt.paths.githubWebPrefix + file + '#L' + line) //TODO: detect search github files
399 | });
400 | }
401 |
402 | data = data.replace(fullStack[0], '');
403 | }
404 | } else {
405 | if (meta_matches && meta_matches.length === 5) {
406 | output.server = meta_matches[1];
407 | output.timestamp = new Date(meta_matches[2]);
408 | output.severity = meta_matches[3];
409 | output.client = meta_matches[4];
410 |
411 | // create a mapping of groups to names to be used as classnames
412 | var meta_named_groups = {
413 | 'server' : meta_matches[1],
414 | 'timestamp' : meta_matches[2],
415 | 'apache_info' : meta_matches[3],
416 | 'client' : meta_matches[4]
417 | };
418 | }
419 |
420 | //TODO: change the way this is rendered
421 | for (var key in meta_named_groups) {
422 | regex = new RegExp('(' + meta_named_groups[key].replace('[', '\\[').replace(']', '\\]') + ')', 'g');
423 | data = data.replace(regex, '$1');
424 | }
425 | }
426 | } else {
427 | //Failed to recognize format so make a generic entry
428 | //TODO: change the way this is rendered
429 | data = data + (name ? '[' + name + '] ' : '');
430 | }
431 |
432 | //Linkify any URLs floating around
433 | data = data.replace(pvt.regex.url, "$1");
434 |
435 | //Save token if defined
436 | if (token_matches) {
437 | output.token = token_matches[1];
438 | }
439 |
440 | //Linkify any sourcecode references
441 | data = data.replace(pvt.regex.codepath, '$3:$4$5$6');
442 |
443 | //Mark PHP fatal errors
444 | if (output.rawdata.match(pvt.regex.phpFatal)) {
445 | output.isFatal = true;
446 | }
447 |
448 | //Remove any extraneous whitespace
449 | output.data = $.trim(data);
450 |
451 | return output;
452 |
453 | } catch (ex) { console.log(ex); }
454 | };
455 |
456 | sg.writeLogEntry = function Etsy$Supergrep$writeLogEntry (ns, data) {
457 | var dataHash = hex_sha256(data);
458 | if (pvt.hashes[dataHash]) {
459 | return;
460 | }
461 | var templateData = this.parseLogEntry(ns, data);
462 | pvt.hashes[dataHash] = 1;
463 | templateData.hash = dataHash;
464 | this.addEntry($(CWinberry.Templating.render('tpl_logEntry', templateData)), templateData);
465 | };
466 |
467 | sg.writeLogEntries = function Etsy$Supergrep$writeLogEntries (entries) {
468 | $.each(entries.lines, function (index, value) {
469 | sg.writeLogEntry(entries.name, value);
470 | });
471 | };
472 |
473 | })(window, jQuery);
474 |
--------------------------------------------------------------------------------
/static/js/supergrep.min.js:
--------------------------------------------------------------------------------
1 | // Log streaming
2 | (function(a,b){function e(a){c.writeMsg("Lost connection... retrying in "+a+" seconds"),setTimeout(function(){d.socket.socket.connected||(d.socket=io.connect())},a*1e3),setTimeout(function(){!d.socket.socket.connected&&!d.socket.socket.connecting&&e(a*2)},(a+3)*1e3)}a.console=a.console||{log:function(){}},a.Etsy=a.Etsy||{},a.Etsy.Supergrep=a.Etsy.Supergrep||{};var c=a.Etsy.Supergrep;c.SortOrder={Ascending:"asc",Descending:"dsc"};var d={socket:null,targetLogs:[],logEntries:[],entryCounter:0,hashes:{},unseen:0,config:{maxEntries:500,sortOrder:c.SortOrder.Descending,autoscroll:!0,filter:null,highlight:null,background:!1,pageTitle:"Supergrep++"},paths:{githubWebPrefix:"https://github.com/your/repo/blob/master/"},regex:{meta:/^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s\[([^\]]+)\]\s\[(?:client\s*)?([^\]]+)\]/i,meta_dev:/^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s\[([^\]]+)\]\s\[(?:client\s*)?([^\]]+)\]/i,web:/^((?:web)[\d]+)\s*\:?\s*\[([^\]]+)\]\s(?:\[[^\]]+\])\s\[(?:client\s*)?([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s+/i,web_dev:/^()\[([^\\[\]]+)\]\s()\[([^\\[\]]+)\]\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+/i,gearman:/^((?:web|preprod\-web|atlasweb|api|worker)[\d]+)\s*\:?\s*\[([^\]]+)\]\s()\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s+/i,gearman_dev:/^()\[([^\]]+)\]\s()\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s\[([^\]]+)\]\s+/i,error:/\[(error)\]/ig,warning:/\[(warning)\]/ig,info:/\[((info|notice))\]/ig,debug:/\[(debug)\]/ig,phpFatal:/PHP Fatal/i,stacktrace:/(\s#\d+\s)/g,fullStacktrace:/(?:stack trace:\s+|backtrace:(?:\\n|\s)*)(#\d+.+\.php(?:\:\d+|\(\d+\)))/i,stackSplitter:/\s*#\d+\s+/,stackFile:/(\/your\/path\/here\/|\/home\/[^\\\/]+)/i,phpFile:/\.php/i,stackInternalFunc:/\[internal\sfunction\]:\s*/i,stackFileLine:/\:\d+$/,stackAltFileLine:/\.php\((\d+)\):/i,codepath:/((\/your\/path\/here\/)([^\.]+.php)(?:\(([\d]+)\)|(?:\s*on)?\s+line\s+(\d+)|:(\d+)))/i,devtest:/^\[/,token:/\[([a-z0-9-_]{28})\]/i,severity:/(?:\[[a-z0-9-_]{28}\])\s\[(error|warning)\]/i,fileNotFound:/File does not exist/i,url:/((?:https?:\/\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig}};c.submitGist=function(c,d,e){b("#gistform-filename").val("supergrep["+c+"]"+d),b("#gistform-data").val(unescape(e)),b("#gistform").get(0).submit()},c.submitIrc=function(c,d){c=b.trim(c);if(!c)return;if(!c.match(/^(@|#)/))return;var e=c.split(" ");c=e.shift(),d=e.join(" ")+"\n"+d,b.ajax("/v2/irccat",{type:"POST",data:{target:c,data:d}})},c.watchLogs=function(f,g){d.subscribedLogs=f,d.unsubscribedLogs=g,d.socket?d.socket.emit("subscriptions",{subscribeTo:d.subscribedLogs,unsubscribeFrom:d.unsubscribedLogs}):(d.socket=io.connect(document.location.hostname,{transports:["xhr-polling"]}),d.socket.on("connect",function(){b("#loading").hide(),c.writeMsg("Connection established"),d.socket.emit("subscriptions",{subscribeTo:d.subscribedLogs,unsubscribeFrom:d.unsubscribedLogs})}),d.socket.on("lines",function(a){c.writeLogEntries(a)}),d.socket.on("disconnect",function(){d.socket.connected||e(2)}))},c.setBackground=function(c){d.config.background=!!c,d.config.background||(d.unseen=0,b(document).attr("title",d.config.pageTitle))},c.parseFilterRule=function(c){c=b.trim(c);if(c==="")return null;var d=c.match(/^\/(.+)\/([igsm]*)$/);if(d)return new RegExp(d[1],d[2]);c=c.replace(/([\\\/\.\*\+\?\(\)\[\]\{\}\-\|])/g,"\\$1");var e=c.split(/(?:\r\n|\n|\,)+/),f=[];for(var g=0,h=e.length;gd.config.maxEntries){var b=d.logEntries.shift();b.element.remove(),b.extra.hash&&delete d.hashes[b.extra.hash]}},c.displayEntry=function(e){d.config.sortOrder===this.SortOrder.Ascending?b(e).appendTo("#results-list"):b(e).prependTo("#results-list"),c.scrollLog(b(e))},c.addEntry=function(e,f){e=b(e);var g={element:e,extra:f},h=c.evalLogEntry(g);d.logEntries.push(g),this.displayEntry(e),this.trimEntries(),h&&d.config.background&&(d.unseen++,b(document).attr("title","("+d.unseen+" new) "+d.config.pageTitle))},c.clearEntries=function(){d.logEntries=[],b("#results-list").empty()},c.writeMsg=function(c){var e={id:"msg"+d.entryCounter++,data:c,rawdata:c};this.addEntry(b(CWinberry.Templating.render("tpl_msgEntry",e)),e)},c.parseLogEntry=function(c,e){try{var f={ns:c,rawdata:e,id:"log"+d.entryCounter++},g=d.regex.devtest.test(e),h=e.match(d.regex.meta),i=e.match(d.regex.error),j=e.match(d.regex.warning),k=e.match(d.regex.token),l=e.match(d.regex.severity),m=l?l[1]:"info";if(c==="web"){var n=e.match(c==="gearman"?g?d.regex.gearman_dev:d.regex.gearman:g?d.regex.web_dev:d.regex.web);if(n&&n.length===9){e=e.replace(n[0],""),f.server=n[1]||"localhost",f.timestamp=new Date(n[2]),isNaN(f.timestamp.getTime())&&(f.timestamp=new Date(0)),f.client=n[3]||"127.0.0.1",f.hash=n[4],f.severity=n[5],f.namespace=n[6];var o=n[7]?n[7].split(":"):["",0];f.path={url:n[7].replace(d.regex.codepath,d.paths.githubWebPrefix+"$3#L$4$5$6"),file:o[0].replace(d.regex.stackFile,""),line:o[1]},f.userid=n[8]&&n[8]!=="0"?n[8]:"";var p=e.match(d.regex.fullStacktrace);if(p){f.stacktrace=[];var q=p[1].split(d.regex.stackSplitter);q.shift();for(var r=0,s=q.length;r$1')}}else e=e+(name?'['+name+"] ":"")+e;return e=e.replace(d.regex.url,"$1"),k&&(f.token=k[1]),e=e.replace(d.regex.codepath,'$3:$4$5$6'),f.rawdata.match(d.regex.phpFatal)&&(f.isFatal=!0),f.data=b.trim(e),f}catch(B){console.log(B)}},c.writeLogEntry=function(c,e){var f=hex_sha256(e);if(d.hashes[f])return;var g=this.parseLogEntry(c,e);d.hashes[f]=1,g.hash=f,this.addEntry(b(CWinberry.Templating.render("tpl_logEntry",g)),g)},c.writeLogEntries=function(d){b.each(d.lines,function(a,b){c.writeLogEntry(d.name,b)})}})(window,jQuery);
--------------------------------------------------------------------------------
/static/js/templating.js:
--------------------------------------------------------------------------------
1 | (function (window, $) {
2 | window.console = window.console || { log: function () {} };
3 |
4 | window.CWinberry = window.CWinberry || {};
5 | window.CWinberry.Templating = window.CWinberry.Templating || {};
6 |
7 | ct = window.CWinberry.Templating;
8 |
9 | var templateCache = {};
10 |
11 | ct.render = function CWinberry$Templating$render (templateId, data) {
12 | return this.loadTemplate(templateId)(data);
13 | };
14 |
15 | ct.loadTemplate = function CWinberry$Templating$loadTemplate (templateId) {
16 | if (templateCache[templateId]) {
17 | return templateCache[templateId];
18 | }
19 | var templateSrc = $('#' + templateId);
20 | if (templateSrc.length === 0) {
21 | throw "Unrecognized template ID: " + templateId;
22 | }
23 | templateCache[templateId] = this.compile(templateSrc.text());
24 | return templateCache[templateId];
25 | };
26 |
27 | ct.compile = function CWinberry$Templating$compile (template) {
28 | function addText (buffer, text, unescaped) {
29 | unescaped = !!unescaped;
30 | buffer.push("\tprint(");
31 | buffer.push(unescaped ? text : "\"" + (text
32 | .split("\r").join("\\r")
33 | .split("\n").join("\\n")
34 | .split("\t").join("\\t")
35 | .split("\"").join("\\\"")
36 | ) + "\""
37 | );
38 | buffer.push(");\n");
39 | }
40 |
41 | function addCode (buffer, code) {
42 | if (code.indexOf("=") === 0) {
43 | addText(buffer, code.substring(1, code.length), true);
44 | } else {
45 | buffer.push("\t" + code);
46 | }
47 | }
48 |
49 | function renderTemplate (templateId, data) {
50 | addText(ct.render(templateId, data));
51 | }
52 |
53 | var buffer = [];
54 | var re = /(<%|%>)/g;
55 | var prvPos = 0;
56 | var prvSep = "";
57 | while (re.test(template)) {
58 | var curPos = re.lastIndex;
59 | var curSep = template.substring(curPos - 2, curPos);
60 | if (curSep == "<%") {
61 | addText(buffer, template.substring(prvPos, curPos - 2));
62 | } else { //curSep == "%>"
63 | addCode(buffer, template.substring(prvPos, curPos - 2));
64 | }
65 | prvPos = curPos;
66 | prvSep = curSep;
67 | }
68 | if (prvPos < template.length) {
69 | if (prvSep === "%>" || prvSep === "") {
70 | addText(buffer, template.substring(prvPos, template.length));
71 | } else { //prvSep == "%>"
72 | addCode(buffer, template.substring(prvPos, template.length));
73 | }
74 | }
75 |
76 | var src =
77 | " var __output = [];\n" +
78 | " var print = function () {\n" +
79 | " __output.push.apply(__output, arguments);\n"+
80 | " };\n" +
81 | " var renderTemplate = function (templateId, data) {\n" +
82 | " print(CWinberry.Templating.render(templateId, data));\n"+
83 | " };\n" +
84 | buffer.join('') +
85 | " return(__output.join(''));"
86 | ;
87 |
88 | var templateFunc = null;
89 | try {
90 | templateFunc = new Function("data", src);
91 | } catch (ex) {
92 | throw "Failed to compile template: " + ex;
93 | }
94 | return templateFunc;
95 | };
96 |
97 | })(window, jQuery);
98 |
--------------------------------------------------------------------------------
/static/js/templating.min.js:
--------------------------------------------------------------------------------
1 | (function(c,h){c.console=c.console||{log:function(){}};c.CWinberry=c.CWinberry||{};c.CWinberry.Templating=c.CWinberry.Templating||{};ct=c.CWinberry.Templating;var f={};ct.render=function(a,d){return this.loadTemplate(a)(d)};ct.loadTemplate=function(a){if(f[a])return f[a];var d=h("#"+a);if(0===d.length)throw"Unrecognized template ID: "+a;f[a]=this.compile(d.text());return f[a]};ct.compile=function(a){function d(a,c,b){b=!!b;a.push("\tprint(");a.push(b?c:'"'+c.split("\r").join("\\r").split("\n").join("\\n").split("\t").join("\\t").split('"').join('\\"')+ '"');a.push(");\n")}function c(a,b){0===b.indexOf("=")?d(a,b.substring(1,b.length),!0):a.push("\t"+b)}for(var e=[],f=/(<%|%>)/g,g=0,b="";f.test(a);){var b=f.lastIndex,i=a.substring(b-2,b);"<%"==i?d(e,a.substring(g,b-2)):c(e,a.substring(g,b-2));g=b;b=i}g"===b||""===b?d(e,a.substring(g,a.length)):c(e,a.substring(g,a.length)));a=" var __output = [];\n var print = function () {\n __output.push.apply(__output, arguments);\n };\n var renderTemplate = function (templateId, data) {\n print(CWinberry.Templating.render(templateId, data));\n };\n"+ e.join("")+" return(__output.join(''));";e=null;try{e=new Function("data",a)}catch(h){throw"Failed to compile template: "+h;}return e}})(window,jQuery);
--------------------------------------------------------------------------------
/static/js/underscore.min.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.2.2
2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
3 | // Underscore is freely distributable under the MIT license.
4 | // Portions of Underscore are inspired or borrowed from Prototype,
5 | // Oliver Steele's Functional, and John Resig's Micro-Templating.
6 | // For all details and documentation:
7 | // http://documentcloud.github.com/underscore
8 | (function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(b.isFunction(a.isEqual))return a.isEqual(c);if(b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return String(a)==String(c);case "[object Number]":return a=+a,c=+c,a!=a?c!=c:a==0?1/a==1/c:a==c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c,
10 | h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,H=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&&define.amd?
11 | define("underscore",function(){return b}):s._=b;b.VERSION="1.2.2";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,c){var b=e(a,c);(d[b]||(d[b]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<
17 | f;){var g=e+f>>1;d(a[g])=0})})};b.difference=function(a,c){return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=H||function(a){if(a!==
24 | Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?
25 | a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=l.call(arguments)=="[object Arguments]"?function(a){return l.call(a)=="[object Arguments]"}:
26 | function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};
27 | b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),function(c){I(c,b[c]=a[c])})};var J=0;b.uniqueId=function(a){var b=J++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,
28 | interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,
29 | "\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e(a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},I=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);G.call(a,this._wrapped);return u(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped,
30 | arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this);
31 |
--------------------------------------------------------------------------------
/stream.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var qs = require('querystring');
3 | var net = require('net');
4 | var path = require('path');
5 | var exec = require("child_process").exec;
6 | var express = require('express');
7 | var socketio = require('socket.io');
8 | var less = require('less');
9 | var uglify = require('uglify-js');
10 | var LogReader = require('./lib/logreader');
11 |
12 | var STATIC_PATH = '/static';
13 |
14 | var errorLines = [];
15 | var logReaders = {};
16 | var cache = { js: {}, jsc: {}, less: {} };
17 |
18 | /* Config stuff */
19 | //Load config specified on command line
20 | var config = require(__dirname + "/" + process.argv[2]).config;
21 | if (!config.defaultMaxLines) {
22 | config.defaultMaxLines = 50;
23 | }
24 | //Set NODE_ENV based on config 'dev' flag; used by Express
25 | process.env.NODE_ENV = config.dev ? 'development' : 'production';
26 |
27 | //Create a log reader for each log defined in config
28 | config.files.forEach(function (file) {
29 | logReaders[file.name] = new LogReader(file, config);
30 | });
31 |
32 | // trap TERM signals and close all readers
33 | process.on('SIGTERM', function() {
34 | closeReaders();
35 | process.exit(0);
36 | });
37 |
38 | /* Misc helper funcs */
39 | function closeReaders() {
40 | for (var name in logReaders) {
41 | logReaders[name].log.kill();
42 | }
43 | }
44 |
45 | function fileExists(path, cb) {
46 | fs.stat(path, function (err, stat) {
47 | cb(!err && stat.isFile());
48 | });
49 | }
50 |
51 | function dirExistsSync (path) {
52 | try {
53 | return fs.statSync(path).isDirectory();
54 | } catch (ex) {
55 | return false;
56 | }
57 | }
58 |
59 | var app = express.createServer();
60 | //Allow JSONP support
61 | app.enable("jsonp callback");
62 | app.use(express.logger());
63 | app.use(express.bodyParser());
64 | app.use(express.query());
65 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
66 |
67 | //IRCCat proxy
68 | app.post('/irccat', function (req, res, next) {
69 | var postData = '';
70 | req.on('data', function (chunk) {
71 | postData += chunk;
72 | });
73 | req.on('end', function () {
74 | var params = qs.parse(postData);
75 | res.end('OK');
76 | if (params.target && params.data) {
77 | //TODO: fixed the double prefix
78 | params.data = config.irccat.prefix + params.data
79 | .replace(/(^[\s\r\n]|[\s\r\n]$)/g)
80 | .match(new RegExp('.{1,' + (config.irccat.maxchars - config.irccat.prefix.length) + '}', 'g'))
81 | .join("\n")
82 | .replace(/[\r\n]+/g, "\n" + config.irccat.prefix)
83 | ;
84 | var telnet = new net.Socket();
85 | telnet.on('connect', function () {
86 | telnet.end(params.target + ' ' + params.data + "\n");
87 | })
88 | .connect(config.irccat.port, config.irccat.host);
89 | }
90 | });
91 | });
92 | //Compiles LESS files to CSS
93 | app.get('/*.css', function (req, res, next) {
94 | var cssFilename = __dirname + STATIC_PATH + req.url;
95 | fileExists(cssFilename, function (exists) {
96 | if (exists) {
97 | return next();
98 | }
99 | var lessFilename = cssFilename.replace(/\.css$/, '.less');
100 | fileExists(lessFilename, function (exists) {
101 | if (!exists) {
102 | return next();
103 | }
104 | if (cache.less[lessFilename]) {
105 | res.contentType('foo.css');
106 | return res.end(cache.less[lessFilename]);
107 | }
108 | fs.readFile(lessFilename, function (err, lessData) {
109 | if (err) {
110 | return next();
111 | }
112 | lessData = lessData.toString();
113 | var pathParts = lessFilename.split('/');
114 | var filename = pathParts.pop();
115 | var lessPath = pathParts.join('/');
116 | var parser = new less.Parser ({
117 | paths: [lessPath]
118 | , filename: filename
119 | });
120 | parser.parse(lessData, function (err, tree) {
121 | if (err) {
122 | return next();
123 | }
124 | var cssData = tree.toCSS({ compress: !config.dev });
125 | if (!config.dev) {
126 | cache.less[lessFilename] = cssData;
127 | }
128 | res.contentType('foo.css');
129 | return res.end(cssData);
130 | });
131 | });
132 | });
133 | });
134 | });
135 | //JS compiler/compressor
136 | app.get('/*.jsc', function (req, res, next) {
137 | var jscFilename = __dirname + STATIC_PATH + req.url;
138 | fileExists(jscFilename, function (exists) {
139 | if (!exists) {
140 | return next();
141 | }
142 | if (cache.jsc[jscFilename]) {
143 | res.contentType('foo.js');
144 | return res.end(cache.jsc[jscFilename]);
145 | }
146 | fs.readFile(jscFilename, function (err, jscData) {
147 | if (err) {
148 | return next();
149 | }
150 | jscData = jscData.toString();
151 | var pathParts = jscFilename.split('/');
152 | pathParts.pop();
153 | var jsIncludePath = pathParts.join('/') + '/';
154 | var files = jscData.replace(/(^\s+|\s+$)/g, '').split(/\n+/g);
155 |
156 | var output = [];
157 | var jsReader = function () {
158 | if (files.length) {
159 | var includeFile = files.shift().replace(/(^\s+|\s+$)/g, '');
160 | if (includeFile === '') {
161 | return jsReader();
162 | }
163 | var filePath = jsIncludePath + includeFile;
164 | fs.readFile(filePath, function (err, jsData) {
165 | if (err) {
166 | console.log("Error loading JS file", filePath, err);
167 | } else {
168 | jsData = jsData.toString();
169 | if (!config.dev) {
170 | var tree = uglify.parser.parse(jsData);
171 | tree = uglify.uglify.ast_squeeze(tree);
172 | jsData = uglify.uglify.gen_code(tree);
173 | }
174 | output.push("\n/* JS include: " + includeFile + " */\n");
175 | output.push(jsData);
176 | }
177 | jsReader();
178 | });
179 | } else {
180 | var compiledOutput = output.join("\n;\n");
181 | if (!config.dev) {
182 | cache.jsc[jscFilename] = compiledOutput;
183 | }
184 | res.contentType('foo.js');
185 | return res.end(compiledOutput);
186 | }
187 | };
188 | jsReader();
189 | });
190 | });
191 | });
192 | //Blamebot functionality
193 | app.get(config.blamebot.uri_match_regexp, function (req, res, next) {
194 | console.log('DEB', req.params);
195 | var reponame = req.params[0].toLowerCase();
196 | var filename = req.params[1];
197 | var startline = req.params[2];
198 | var endline = req.params[3] || startline;
199 |
200 | var cmd = config.blamebot.git_command + ' ' +
201 | config.blamebot.git_blame_options.join(' ') +
202 | " -L " + startline + "," + endline + " " +
203 | " -- " + filename;
204 |
205 | console.log('REQUEST', reponame, config.blamebot.git_checkout_dirs[reponame].path, filename, startline, endline);
206 |
207 | if (!config.blamebot.git_checkout_dirs[reponame]) {
208 | res.json({ error: {
209 | msg: 'Requested repository not found'
210 | }});
211 | return;
212 | }
213 |
214 | var child = exec(cmd, {"cwd" : config.blamebot.git_checkout_dirs[reponame].path},
215 | function (error, stdout, stderr) {
216 | if (error) {
217 | res.json({ error: {
218 | msg: error
219 | , details: stderr
220 | }});
221 | return;
222 | }
223 |
224 | var lines = stdout.split('\n');
225 | var details = {};
226 | for (var i = 0, len = lines.length; i < len; i++) {
227 | var line = lines[i].replace(/(^\s+|\s+^)/g).split(/\s/);
228 | if (line.length < 2) {
229 | continue;
230 | }
231 | var key = line.shift();
232 | details[key] = line.join(' ');
233 | }
234 |
235 | res.json(details);
236 | });
237 | });
238 | //Redirect legacy /v2/* paths to /
239 | app.all('/v2/:respath?', function (req, res, next) {
240 | var qs = req.url.split('?');
241 | qs.shift();
242 | res.redirect(
243 | '/' +
244 | (req.params.respath ? req.params.respath : '') +
245 | (qs.length ? '?' + qs.join('?') : '')
246 | );
247 | });
248 | app.use(express.staticCache());
249 | app.use(express.static(__dirname + STATIC_PATH));
250 | server = app.listen(config.port);
251 |
252 | var websocket = socketio.listen(server);
253 |
254 | websocket.sockets.on('connection', function (client) {
255 | var self = client;
256 |
257 | client.listeners = {};
258 |
259 | client.on('subscriptions', function(data) {
260 | if (data.subscribeTo) {
261 | data.subscribeTo.forEach(function (feed) {
262 | if (logReaders[feed] && !self.listeners[feed]) {
263 | //console.log("gonna send" + util.inspect(logReaders[feed].lines));
264 | self.emit('lines', {name: feed, lines: logReaders[feed].lines});
265 |
266 | var listener = function (line) {
267 | self.emit('lines', {name: feed, lines: [line]});
268 | };
269 |
270 | self.listeners[feed] = listener;
271 |
272 | logReaders[feed].on("line", listener);
273 | }
274 | });
275 | }
276 |
277 | if (data.unsubscribeFrom) {
278 | data.unsubscribeFrom.forEach(function (feed) {
279 | if (self.listeners[feed]) {
280 | logReaders[feed].removeListener("line", self.listeners[feed]);
281 | delete self.listeners[feed];
282 | }
283 | });
284 | }
285 | });
286 |
287 | // remove the listeners so they don't just take up memory
288 | client.on('disconnect', function () {
289 | for (var feed in self.listeners) {
290 | logReaders[feed].removeListener("line", self.listeners[feed]);
291 | delete self.listeners[feed];
292 | }
293 | });
294 |
295 | });
296 |
297 | //Bootstrap for create/updating repos for blamebot
298 | (function () {
299 | var key;
300 | var dir;
301 |
302 | function genUpdateRepoCallback (name) {
303 | return function (error, stdout, stderr) {
304 | setTimeout(function () { updateRepo(name); }, config.blamebot.refresh_time);
305 | console.log('UPDATE (' + name + ')', [error, stdout, stderr]);
306 | };
307 | }
308 |
309 | function updateRepo (name) {
310 | exec(config.blamebot.git_command + ' ' + config.blamebot.git_pull_options.join(' ')
311 | , { 'cwd' : config.blamebot.git_checkout_dirs[name].path }
312 | , genUpdateRepoCallback(name)
313 | );
314 | }
315 |
316 | var entry;
317 | var repoPath;
318 | for (key in config.blamebot.git_checkout_dirs) {
319 | entry = config.blamebot.git_checkout_dirs[key];
320 | repoPath = path.normalize(entry.path);
321 | if (dirExistsSync(repoPath)) {
322 | updateRepo(key);
323 | } else {
324 | if (config.git_clone_enabled) {
325 | exec(config.blamebot.git_command + ' ' + config.blamebot.git_clone_options.join(' ') + ' ' + entry.git + ' ' + repoPath
326 | , genUpdateRepoCallback(key)
327 | );
328 | }
329 | }
330 | }
331 | })();
332 |
--------------------------------------------------------------------------------
/supergrep.init:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # supergrep initd script
3 | #
4 | # chkconfig: - 50 50
5 | # description: Startup script for supergrep
6 | #
7 | # processname: /usr/bin/node
8 | # pidfile: /var/run/stream.pid
9 |
10 | # source function library
11 | . /etc/init.d/functions
12 |
13 | OPTIONS="/path/to/supergrep/stream.js prodConfig.js"
14 | RETVAL=0
15 | prog="node stream.js prodConfig.js"
16 | pidfile="/var/run/stream.pid"
17 |
18 | start() {
19 | echo -n $"Starting $prog: "
20 | if [ $UID -ne 0 ]; then
21 | RETVAL=1
22 | failure
23 | else
24 | daemon /usr/bin/node $OPTIONS &>/dev/null &
25 | RETVAL=$?
26 | [ $RETVAL -eq 0 ] && touch /var/lock/subsys/stream
27 | fi;
28 | echo
29 | return $RETVAL
30 | }
31 |
32 | stop() {
33 | echo -n $"Stopping $prog: "
34 | if [ $UID -ne 0 ]; then
35 | RETVAL=1
36 | failure
37 | else
38 | pkill -f "$OPTIONS"
39 | RETVAL=$?
40 | [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/stream
41 | fi;
42 | echo
43 | return $RETVAL
44 | }
45 |
46 | restart(){
47 | stop
48 | start
49 | }
50 |
51 | case "$1" in
52 | start)
53 | start
54 | ;;
55 | stop)
56 | stop
57 | ;;
58 | restart)
59 | restart
60 | ;;
61 | status)
62 | status -p $pidfile /usr/bin/node
63 | RETVAL=$?
64 | ;;
65 | *)
66 | echo $"Usage: $0 {start|stop|status|restart}"
67 | RETVAL=1
68 | esac
69 |
70 | exit $RETVAL
71 |
--------------------------------------------------------------------------------