]> git.openstreetmap.org Git - osqa.git/blob - forum/skins/default/media/js/com.cnprog.post.js
deleting the test file
[osqa.git] / forum / skins / default / media / js / com.cnprog.post.js
1 /*
2 Scripts for cnprog.com
3 Project Name: Lanai
4 All Rights Resevred 2008. CNPROG.COM
5 */
6 var lanai =
7 {
8     /**
9      * Finds any <pre><code></code></pre> tags which aren't registered for
10      * pretty printing, adds the appropriate class name and invokes prettify.
11      */
12     highlightSyntax: function(){
13         var styled = false;
14         $("pre code").parent().each(function(){
15             if (!$(this).hasClass('prettyprint')){
16                 $(this).addClass('prettyprint');
17                 styled = true;
18             }
19         });
20
21         if (styled){
22             prettyPrint();
23         }
24     }
25 };
26
27 var Vote = function(){
28     // All actions are related to a question
29     var questionId;
30     //question slug to build redirect urls
31     var questionSlug;
32     // The object we operate on actually. It can be a question or an answer.
33     var postId;
34     var questionAuthorId;
35     var currentUserId;
36     var answerContainerIdPrefix = 'answer-container-';
37     var voteContainerId = 'vote-buttons';
38     var imgIdPrefixAccept = 'answer-img-accept-';
39     var imgClassPrefixFavorite = 'question-img-favorite';
40     var imgIdPrefixQuestionVoteup = 'question-img-upvote-';
41     var imgIdPrefixQuestionVotedown = 'question-img-downvote-';
42     var imgIdPrefixAnswerVoteup = 'answer-img-upvote-';
43     var imgIdPrefixAnswerVotedown = 'answer-img-downvote-';
44     var divIdFavorite = 'favorite-number';
45     var commentLinkIdPrefix = 'comment-';
46     var voteNumberClass = "vote-number";
47     var offensiveIdPrefixQuestionFlag = 'question-offensive-flag-';
48     var offensiveIdPrefixAnswerFlag = 'answer-offensive-flag-';
49     var offensiveClassFlag = 'offensive-flag';
50     var questionControlsId = 'question-controls';
51     var removeQuestionLinkIdPrefix = 'question-delete-link-';
52     var removeAnswerLinkIdPrefix = 'answer-delete-link-';
53     var questionSubscribeUpdates = 'question-subscribe-updates';
54     
55     var acceptAnonymousMessage = $.i18n._('insufficient privilege');
56     var acceptOwnAnswerMessage = $.i18n._('cannot pick own answer as best');
57
58     var pleaseLogin = "<a href='" + scriptUrl + $.i18n._("account/") + $.i18n._("signin/")
59                     + "?next=" + scriptUrl + $.i18n._("question/") + "{{QuestionID}}/{{questionSlug}}'>"
60                                         + $.i18n._('please login') + "</a>";
61
62     var pleaseSeeFAQ = $.i18n._('please see') + "<a href='" + scriptUrl + $.i18n._("faq/") + "'>faq</a>";
63
64     var favoriteAnonymousMessage = $.i18n._('anonymous users cannot select favorite questions');
65     var voteAnonymousMessage = $.i18n._('anonymous users cannot vote') + pleaseLogin;
66     var upVoteRequiredScoreMessage = $.i18n._('>15 points requried to upvote') + pleaseSeeFAQ;
67     var downVoteRequiredScoreMessage = $.i18n._('>100 points required to downvote') + pleaseSeeFAQ;
68     var voteOwnDeniedMessage = $.i18n._('cannot vote for own posts');
69     var voteRequiredMoreVotes = $.i18n._('daily vote cap exhausted') + pleaseSeeFAQ;
70     var voteDenyCancelMessage = $.i18n._('cannot revoke old vote') + pleaseSeeFAQ;
71     var offensiveConfirmation = $.i18n._('please confirm offensive');
72     var offensiveAnonymousMessage = $.i18n._('anonymous users cannot flag offensive posts') + pleaseLogin;
73     var offensiveTwiceMessage = $.i18n._('cannot flag message as offensive twice') + pleaseSeeFAQ;
74     var offensiveNoFlagsLeftMessage = $.i18n._('flag offensive cap exhausted') + pleaseSeeFAQ;
75     var offensiveNoPermissionMessage = $.i18n._('need >15 points to report spam') + pleaseSeeFAQ;
76     var removeConfirmation = $.i18n._('confirm delete');
77     var removeAnonymousMessage = $.i18n._('anonymous users cannot delete/undelete');
78     var recoveredMessage = $.i18n._('post recovered');
79     var deletedMessage = $.i18n._('post deleted');
80     
81     var VoteType = {
82         acceptAnswer : 0,
83         questionUpVote : 1,
84         questionDownVote : 2,
85         favorite : 4,
86         answerUpVote: 5,
87         answerDownVote:6,
88         offensiveQuestion : 7,
89         offensiveAnswer:8,
90         removeQuestion: 9,
91         removeAnswer:10,
92         questionSubscribeUpdates:11,
93         questionUnsubscribeUpdates:12
94     };
95
96     var getFavoriteButton = function(){
97         var favoriteButton = 'div.'+ voteContainerId +' img[class='+ imgClassPrefixFavorite +']';
98         return $(favoriteButton);
99     };
100     var getFavoriteNumber = function(){
101         var favoriteNumber = '#'+ divIdFavorite ;
102         return $(favoriteNumber);
103     };
104     var getQuestionVoteUpButton = function(){
105         var questionVoteUpButton = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixQuestionVoteup +']';
106         return $(questionVoteUpButton);
107     };
108     var getQuestionVoteDownButton = function(){
109         var questionVoteDownButton = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixQuestionVotedown +']';
110         return $(questionVoteDownButton);
111     };
112     var getAnswerVoteUpButtons = function(){
113         var answerVoteUpButton = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAnswerVoteup +']';
114         return $(answerVoteUpButton);
115     };
116     var getAnswerVoteDownButtons = function(){
117         var answerVoteDownButton = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAnswerVotedown +']';
118         return $(answerVoteDownButton);
119     };
120     var getAnswerVoteUpButton = function(id){
121         var answerVoteUpButton = 'div.'+ voteContainerId +' img[id='+ imgIdPrefixAnswerVoteup + id + ']';
122         return $(answerVoteUpButton);
123     };
124     var getAnswerVoteDownButton = function(id){
125         var answerVoteDownButton = 'div.'+ voteContainerId +' img[id='+ imgIdPrefixAnswerVotedown + id + ']';
126         return $(answerVoteDownButton);
127     };
128     
129     var getOffensiveQuestionFlag = function(){
130         var offensiveQuestionFlag = '#question-table span[class='+ offensiveClassFlag +']';
131         return $(offensiveQuestionFlag);
132     };
133     
134     var getOffensiveAnswerFlags = function(){
135         var offensiveQuestionFlag = 'div.answer span[class='+ offensiveClassFlag +']';
136         return $(offensiveQuestionFlag);
137     };
138     
139     var getremoveQuestionLink = function(){
140         var removeQuestionLink = 'div#question-controls a[id^='+ removeQuestionLinkIdPrefix +']';
141         return $(removeQuestionLink);
142     };
143
144     var getquestionSubscribeUpdatesCheckbox = function(){
145         return $('#' + questionSubscribeUpdates);
146     };
147     
148     var getremoveAnswersLinks = function(){
149         var removeAnswerLinks = 'div.answer-controls a[id^='+ removeAnswerLinkIdPrefix +']';
150         return $(removeAnswerLinks);
151     };
152    
153     var setVoteImage = function(voteType, undo, object){
154         var flag = undo ? "" : "-on";
155         var arrow = (voteType == VoteType.questionUpVote || voteType == VoteType.answerUpVote) ? "up" : "down";
156         object.attr("src", mediaUrl("media/images/vote-arrow-"+ arrow + flag +".png"));
157         
158         // if undo voting, then undo the pair of arrows.
159         if(undo){
160             if(voteType == VoteType.questionUpVote || voteType == VoteType.questionDownVote){
161                 $(getQuestionVoteUpButton()).attr("src", mediaUrl("media/images/vote-arrow-up.png"));
162                 $(getQuestionVoteDownButton()).attr("src", mediaUrl("media/images/vote-arrow-down.png"));
163             }
164             else{
165                 $(getAnswerVoteUpButton(postId)).attr("src", mediaUrl("media/images/vote-arrow-up.png"));
166                 $(getAnswerVoteDownButton(postId)).attr("src", mediaUrl("media/images/vote-arrow-down.png"));
167             }
168         }
169     };
170     
171     var setVoteNumber = function(object, number){
172         var voteNumber = object.parent('div.'+ voteContainerId).find('div.'+ voteNumberClass);
173         $(voteNumber).text(number);
174     };
175     
176     var bindEvents = function(){
177         // accept answers
178         if(questionAuthorId == currentUserId){
179             var acceptedButtons = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAccept +']';
180             $(acceptedButtons).unbind('click').click(function(event){
181                Vote.accept($(event.target));
182             });
183         }
184         // set favorite question
185         var favoriteButton = getFavoriteButton();
186         favoriteButton.unbind('click').click(function(event){
187            Vote.favorite($(event.target));
188         });
189     
190         // question vote up
191         var questionVoteUpButton = getQuestionVoteUpButton();
192         questionVoteUpButton.unbind('click').click(function(event){
193            Vote.vote($(event.target), VoteType.questionUpVote);
194         });
195     
196         var questionVoteDownButton = getQuestionVoteDownButton();
197         questionVoteDownButton.unbind('click').click(function(event){
198            Vote.vote($(event.target), VoteType.questionDownVote);
199         });
200     
201         var answerVoteUpButton = getAnswerVoteUpButtons();
202         answerVoteUpButton.unbind('click').click(function(event){
203            Vote.vote($(event.target), VoteType.answerUpVote);
204         });
205         
206         var answerVoteDownButton = getAnswerVoteDownButtons();
207         answerVoteDownButton.unbind('click').click(function(event){
208            Vote.vote($(event.target), VoteType.answerDownVote);
209         });
210     
211         getOffensiveQuestionFlag().unbind('click').click(function(event){
212            Vote.offensive(this, VoteType.offensiveQuestion);
213         });
214     
215         getOffensiveAnswerFlags().unbind('click').click(function(event){
216            Vote.offensive(this, VoteType.offensiveAnswer);
217         });
218     
219         getremoveQuestionLink().unbind('click').click(function(event){
220             Vote.remove(this, VoteType.removeQuestion);
221         });
222
223         getquestionSubscribeUpdatesCheckbox().unbind('click').click(function(event){
224             if (this.checked){
225                 Vote.vote($(event.target), VoteType.questionSubscribeUpdates);
226             }
227             else {
228                 Vote.vote($(event.target), VoteType.questionUnsubscribeUpdates);
229             }
230         });
231     
232         getremoveAnswersLinks().unbind('click').click(function(event){
233             Vote.remove(this, VoteType.removeAnswer);
234         });
235     };
236     
237     var submit = function(object, voteType, callback) {
238         $.ajax({
239             type: "POST",
240             cache: false,
241             dataType: "json",
242             url: scriptUrl + $.i18n._("questions/") + questionId + "/" + $.i18n._("vote/"),
243             data: { "type": voteType, "postId": postId },
244             error: handleFail,
245             success: function(data){callback(object, voteType, data);}
246             });
247     };
248     
249     var handleFail = function(xhr, msg){
250         alert("Callback invoke error: " + msg);
251     };
252
253     // callback function for Accept Answer action
254     var callback_accept = function(object, voteType, data){
255         if(data.allowed == "0" && data.success == "0"){
256             showMessage(object, acceptAnonymousMessage);
257         }
258         else if(data.allowed == "-1"){
259             showMessage(object, acceptOwnAnswerMessage);
260         }
261         else if(data.status == "1"){
262             object.attr("src", mediaUrl("media/images/vote-accepted.png"));
263             $("#"+answerContainerIdPrefix+postId).removeClass("accepted-answer");
264             $("#"+commentLinkIdPrefix+postId).removeClass("comment-link-accepted");
265         }
266         else if(data.success == "1"){
267             var acceptedButtons = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAccept +']';
268             $(acceptedButtons).attr("src", mediaUrl("media/images/vote-accepted.png"));
269             var answers = ("div[id^="+answerContainerIdPrefix +"]");
270             $(answers).removeClass("accepted-answer");
271             var commentLinks = ("div[id^="+answerContainerIdPrefix +"] div[id^="+ commentLinkIdPrefix +"]");
272             $(commentLinks).removeClass("comment-link-accepted");
273             
274             object.attr("src", mediaUrl("media/images/vote-accepted-on.png"));
275             $("#"+answerContainerIdPrefix+postId).addClass("accepted-answer");
276             $("#"+commentLinkIdPrefix+postId).addClass("comment-link-accepted");
277         }
278         else{
279             showMessage(object, data.message);
280         }
281     };
282
283     var callback_favorite = function(object, voteType, data){
284         if(data.allowed == "0" && data.success == "0"){
285             showMessage(object, favoriteAnonymousMessage.replace("{{QuestionID}}", questionId));
286         }
287         else if(data.status == "1"){
288             object.attr("src", mediaUrl("media/images/vote-favorite-off.png"));
289             var fav = getFavoriteNumber();
290             fav.removeClass("my-favorite-number");
291             if(data.count === 0){
292                 data.count = '';
293             }
294             fav.text(data.count);
295         }
296         else if(data.success == "1"){
297             object.attr("src", mediaUrl("media/images/vote-favorite-on.png"));
298             var fav = getFavoriteNumber();
299             fav.text(data.count);
300             fav.addClass("my-favorite-number");
301         }
302         else{
303             showMessage(object, data.message);
304         }
305     };
306         
307     var callback_vote = function(object, voteType, data){
308         if(data.allowed == "0" && data.success == "0"){
309             showMessage(object, voteAnonymousMessage.replace("{{QuestionID}}", questionId));
310         }
311         else if (data.allowed == "-3"){
312             showMessage(object, voteRequiredMoreVotes);
313         }
314         else if (data.allowed == "-2"){
315             if (voteType == VoteType.questionUpVote || voteType == VoteType.answerUpVote){
316                 showMessage(object, upVoteRequiredScoreMessage);
317             }
318             else if (voteType == VoteType.questionDownVote || voteType == VoteType.answerDownVote){
319                 showMessage(object, downVoteRequiredScoreMessage);
320             }
321         }
322         else if (data.allowed == "-1"){
323             showMessage(object, voteOwnDeniedMessage);
324         }
325         else if (data.status == "2"){
326             showMessage(object, voteDenyCancelMessage);
327         }
328         else if (data.status == "1"){
329             setVoteImage(voteType, true, object);
330             setVoteNumber(object, data.count);
331         }     
332         else if (data.success == "1"){
333             setVoteImage(voteType, false, object);
334             setVoteNumber(object, data.count);
335             if (data.message.length > 0){
336                 showMessage(object, data.message);
337             }
338         }
339     };
340         
341     var callback_offensive = function(object, voteType, data){
342         object = $(object);
343         if (data.allowed == "0" && data.success == "0"){
344             showMessage(object, offensiveAnonymousMessage.replace("{{QuestionID}}", questionId));
345         }
346         else if (data.allowed == "-3"){
347             showMessage(object, offensiveNoFlagsLeftMessage);
348         }  
349         else if (data.allowed == "-2"){
350             showMessage(object, offensiveNoPermissionMessage);
351         }  
352         else if (data.status == "1"){
353             showMessage(object, offensiveTwiceMessage);
354         }  
355         else if (data.success == "1"){
356             $(object).children('span[class=darkred]').text("("+ data.count +")");
357         }
358     };
359         
360     var callback_remove = function(object, voteType, data){
361         if (data.allowed == "0" && data.success == "0"){
362             showMessage(object, removeAnonymousMessage.replace("{{QuestionID}}", questionId));
363         }
364         else if (data.success == "1"){
365             if (voteType == VoteType.removeQuestion){
366                 window.location.href = scriptUrl + $.i18n._("questions/");
367             }
368             else {
369                 if (removeActionType == 'delete'){
370                     postNode.addClass('deleted');
371                     postRemoveLink.innerHTML = $.i18n._('undelete');
372                     showMessage(object, deletedMessage);
373                 }
374                 else if (removeActionType == 'undelete') {
375                     postNode.removeClass('deleted');
376                     postRemoveLink.innerHTML = $.i18n._('delete');
377                     showMessage(object, recoveredMessage);
378                 }
379             }
380                 }
381     };
382         
383     return {
384         init : function(qId, qSlug, questionAuthor, userId){
385             questionId = qId;
386             questionSlug = qSlug;
387             questionAuthorId = questionAuthor;
388             currentUserId = userId;
389             bindEvents();
390         },
391         
392         // Accept answer public function
393         accept: function(object){
394             postId = object.attr("id").substring(imgIdPrefixAccept.length);
395             submit(object, VoteType.acceptAnswer, callback_accept);
396         },
397         
398         favorite: function(object){
399             if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
400                 showMessage(object, favoriteAnonymousMessage.replace("{{QuestionID}}", questionId));
401                 return false;
402             }
403             submit(object, VoteType.favorite, callback_favorite);
404         },
405             
406         vote: function(object, voteType){
407             if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
408                 showMessage(object, voteAnonymousMessage.replace("{{QuestionID}}", questionId).replace("{{questionSlug}}", questionSlug));
409                 return false;   
410             }
411             if (voteType == VoteType.answerUpVote){
412                 postId = object.attr("id").substring(imgIdPrefixAnswerVoteup.length);
413             }
414             else if (voteType == VoteType.answerDownVote){
415                 postId = object.attr("id").substring(imgIdPrefixAnswerVotedown.length);
416             }
417             
418             submit(object, voteType, callback_vote);
419         },
420         
421         offensive: function(object, voteType){
422             if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
423                 showMessage($(object), offensiveAnonymousMessage.replace("{{QuestionID}}", questionId));
424                 return false;   
425             }
426             if (confirm(offensiveConfirmation)){
427                 postId = object.id.substr(object.id.lastIndexOf('-') + 1);
428                 submit(object, voteType, callback_offensive);
429             }
430         },
431             
432         remove: function(object, voteType){
433             if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
434                 showMessage($(object), removeAnonymousMessage.replace("{{QuestionID}}", questionId));
435                 return false;   
436             }
437             bits = object.id.split('-');
438             postId = bits.pop();/* this seems to be used within submit! */
439             postType = bits.shift();
440
441             var do_proceed = false; 
442             if (postType == 'answer'){
443                 postNode = $('#answer-container-' + postId);
444                 postRemoveLink = object;
445                 if (postNode.hasClass('deleted')){
446                     removeActionType = 'undelete';
447                     do_proceed = true;
448                 }
449                 else {
450                     removeActionType = 'delete';
451                     do_proceed = confirm(removeConfirmation);
452                 }
453             }
454             else {
455                 do_proceed = confirm(removeConfirmation);
456             }
457             if (do_proceed) {
458                 submit($(object), voteType, callback_remove);
459             }
460         }
461     };
462 } ();
463
464 var questionComments = createComments('question');
465 var answerComments = createComments('answer');
466 var commentsFactory = {'question' : questionComments, 'answer' : answerComments};
467
468 // site comments
469 function createComments(type) {
470     var objectType = type;
471     var jDivInit = function(id) {
472         return $("#comments-container-" + objectType + '-' + id);
473     };
474
475     var appendLoaderImg = function(id) {
476         appendLoader("#comments-container-" + objectType + '-' + id);
477     };
478
479     var canPostComments = function(id) {
480         var jHidden = $("#can-post-comments-" + objectType + '-' + id);
481         return jHidden.val().toLowerCase() == "true";
482     };
483
484     var renderForm = function(id) {
485         var formId = "form-comments-" + objectType + "-" + id;
486         var jDiv = $('#comments-link-' + objectType + "-" + id).parent();
487         $(jDiv).css('background','none');
488         $(jDiv).css('padding-left',0);
489         if (canPostComments(id)) {
490             if (jDiv.find("#" + formId).length === 0) {
491                 var form = '<form id="' + formId + '" class="post-comments"><div>';
492                 form += '<textarea name="comment" cols="60" rows="5" maxlength="300" onblur="'+ objectType +'Comments.updateTextCounter(this)" ';
493                 form += 'onfocus="' + objectType + 'Comments.updateTextCounter(this)" onkeyup="'+ objectType +'Comments.updateTextCounter(this)"></textarea>';
494                 form += '<input type="submit" value="' +
495                                                 $.i18n._('add comment') + '" /><br><span class="text-counter"></span>';
496                 form += '<span class="form-error"></span></div></form>';
497
498                 jDiv.append(form);
499
500                 setupFormValidation("#" + formId,
501                     { comment: { required: true, minlength: 10} }, '',
502                     function() { postComment(id, formId); });
503             }
504         }
505         else {
506             var divId = "comments-rep-needed-" + objectType + '-' + id;
507             if (jDiv.find("#" + divId).length === 0) {
508                 jDiv.append('<p id="' + divId + '" class="comment">' + 
509                     $.i18n._('to comment, need') + ' ' +
510                                         repNeededForComments + ' ' + $.i18n._('community karma points') + 
511                     '<a href="' + scriptUrl + $.i18n._('faq/') + '" class="comment-user">' +
512                     $.i18n._('please see') + 'faq</a></span></p>');
513             }
514         }
515     };
516
517     var getComments = function(id, jDiv) {
518         //appendLoaderImg(id);
519         $.getJSON(scriptUrl + objectType + "s/" + id + "/" + $.i18n._("comments/"),
520                 function(json) { showComments(id, json); });
521     };
522
523     var showComments = function(id, json) {
524         var jDiv = jDivInit(id);
525
526         //jDiv = jDiv.find("div.comments");   // this div should contain any fetched comments..
527         //jDiv.find("div[id^='comment-" + objectType + "-'" + "]").remove();  // clean previous calls..
528         jDiv.children().remove();
529         removeLoader();
530         if (json && json.length > 0) {
531             for (var i = 0; i < json.length; i++){
532                 renderComment(jDiv, json[i]);
533             }
534             jDiv.children().show();
535         }
536     };
537
538     var renderDeleteCommentIcon = function(post_id, delete_url){
539         if (canPostComments(post_id)){
540             var html = '';
541             var img = mediaUrl("media/images/close-small.png");
542             var imgHover = mediaUrl("media/images/close-small-hover.png");
543             html += '<img class="delete-icon" onclick="' + objectType + 'Comments.deleteComment($(this), ' + post_id + ', \'' + delete_url + '\')" src="' + img;
544             html += '" onmouseover="$(this).attr(\'src\', \'' + imgHover + '\')" onmouseout="$(this).attr(\'src\', \'' + img;
545             html += '\')" title="' + $.i18n._('delete this comment') + '" />';
546             return html;
547         }
548         else{
549             return '';
550         }
551     };
552
553     // {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null}
554     var renderComment = function(jDiv, json) {
555         var html = '<p id="comment-' + json.id + '" class="comment" style="display:none">' + json.text;
556         html += json.user_url ? ' - <a href="' + json.user_url + '"' : '<span';
557         html += ' class="comment-user">' + json.user_display_name + (json.user_url ? '</a>' : '</span>');
558         html += ' (' + json.comment_age + ')';
559
560         if (json.delete_url){
561             html += renderDeleteCommentIcon(json.object_id, json.delete_url);
562         }
563
564         if (json.delete_url) {
565         }
566
567         html += '</p>';
568
569         jDiv.append(html);
570     };
571
572     var postComment = function(id, formId) {
573         //appendLoaderImg(id);
574
575         var formSelector = "#" + formId;
576         var textarea = $(formSelector + " textarea");
577
578         //todo fix url translations!!!
579         $.ajax({
580             type: "POST",
581             url: scriptUrl + objectType + "s/" + id + "/" + $.i18n._("comments/"),
582             dataType: "json",
583             data: { comment: textarea.val() },
584             success: function(json) {
585                 showComments(id, json);
586                 textarea.val("");
587                 commentsFactory[objectType].updateTextCounter(textarea);
588                 enableSubmitButton(formSelector);
589             },
590             error: function(res, textStatus, errorThrown) {
591                 removeLoader();
592                 showMessage(formSelector, res.responseText);
593                 enableSubmitButton(formSelector);
594             }
595         });
596     };
597
598     // public methods..
599     return {
600
601         init: function() {
602             // Setup "show comments" clicks..
603             $("a[id^='comments-link-" + objectType + "-" + "']").unbind("click").click(function() { 
604                 commentsFactory[objectType].show($(this).attr("id").substr(("comments-link-" + objectType + "-").length)); 
605                 });
606
607             var cBox = $("[id^='comments-container-" + objectType + "']");
608             cBox.each( function(i){
609                 var post_id = $(this).attr('id').replace('comments-container-' + objectType + '-', '');
610                 $(this).children().each(
611                     function(i){
612                         var comment_id = $(this).attr('id').replace('comment-','');
613                         var delete_url = scriptUrl + objectType + 's/' + post_id + '/' +
614                                         $.i18n._('comments/') + comment_id + '/' + $.i18n._('delete/');
615                         var html = $(this).html();
616                         var CommentsClass;
617                         if (objectType == 'question'){
618                             CommentsClass = questionComments;
619                         }
620                         else if (objectType == 'answer') {
621                             CommentsClass = answerComments;
622                         }
623                         var delete_icon = $(this).find('img.delete-icon');
624                         delete_icon.click(function(){CommentsClass.deleteComment($(this),comment_id,delete_url);});
625                         delete_icon.unbind('mouseover').bind('mouseover',
626                             function(){
627                                 $(this).attr('src',mediaUrl('media/images/close-small-hover.png'));
628                             }
629                         );
630                         delete_icon.unbind('mouseout').bind('mouseout',
631                             function(){
632                                 $(this).attr('src',mediaUrl('media/images/close-small.png'));
633                             }
634                         );
635                     }
636                 );
637             });
638         },
639
640         show: function(id) {
641             var jDiv = jDivInit(id);
642             getComments(id, jDiv);
643             renderForm(id);
644             jDiv.show();
645
646             var link = $('#comments-link-' + objectType + '-' + id);
647             if (canPostComments(id)) { link.parent().find("textarea").get(0).focus(); }
648             link.remove();
649         },
650
651         hide: function(id) {
652             var jDiv = jDivInit(id);
653             var len = jDiv.children("div.comments").children().length;
654             var anchorText = len === 0 ? $.i18n._('add a comment') : $.i18n._('comments') + ' (<b>' + len + "</b>)";
655
656             jDiv.hide();
657             jDiv.siblings("a").unbind("click").click(function() { commentsFactory[objectType].show(id); }).html(anchorText);
658             jDiv.children("div.comments").children().hide();
659         },
660
661         deleteComment: function(jImg, id, deleteUrl) {
662             if (confirm($.i18n._('confirm delete comment'))) {
663                 jImg.hide();
664                 $.post(deleteUrl, { dataNeeded: "forIIS7" }, function(json) {
665                     var par = jImg.parent();
666                     par.remove();
667                 }, "json");
668             }
669         },
670
671         updateTextCounter: function(textarea) {
672             var length = textarea.value ? textarea.value.length : 0;
673             var color = length > 270 ? "#f00" : length > 200 ? "#f60" : "#999";
674             var jSpan = $(textarea).siblings("span.text-counter");
675             jSpan.html($.i18n._('can write') +
676                                         (300 - length) + ' ' +
677                                         $.i18n._('characters')).css("color", color);
678         }
679     };
680 }
681
682 $(document).ready(function() {
683     questionComments.init();
684     answerComments.init();
685 });
686
687 /*
688 Prettify
689 http://www.apache.org/licenses/LICENSE-2.0
690 */
691 var PR_SHOULD_USE_CONTINUATION = true; var PR_TAB_WIDTH = 8; var PR_normalizedHtml; var PR; var prettyPrintOne; var prettyPrint; function _pr_isIE6() { var isIE6 = navigator && navigator.userAgent && /\bMSIE 6\./.test(navigator.userAgent); _pr_isIE6 = function() { return isIE6; }; return isIE6; } (function() { function wordSet(words) { words = words.split(/ /g); var set = {}; for (var i = words.length; --i >= 0; ) { var w = words[i]; if (w) { set[w] = null; } } return set; } var FLOW_CONTROL_KEYWORDS = "break continue do else for if return while "; var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " + "double enum extern float goto int long register short signed sizeof " + "static struct switch typedef union unsigned void volatile "; var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " + "new operator private protected public this throw true try "; var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " + "concept concept_map const_cast constexpr decltype " + "dynamic_cast explicit export friend inline late_check " + "mutable namespace nullptr reinterpret_cast static_assert static_cast " + "template typeid typename typeof using virtual wchar_t where "; var JAVA_KEYWORDS = COMMON_KEYWORDS + "boolean byte extends final finally implements import instanceof null " + "native package strictfp super synchronized throws transient "; var CSHARP_KEYWORDS = JAVA_KEYWORDS + "as base by checked decimal delegate descending event " + "fixed foreach from group implicit in interface internal into is lock " + "object out override orderby params readonly ref sbyte sealed " + "stackalloc string select uint ulong unchecked unsafe ushort var "; var JSCRIPT_KEYWORDS = COMMON_KEYWORDS + "debugger eval export function get null set undefined var with " + "Infinity NaN "; var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " + "goto if import last local my next no our print package redo require " + "sub undef unless until use wantarray while BEGIN END "; var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " + "elif except exec finally from global import in is lambda " + "nonlocal not or pass print raise try with yield " + "False True None "; var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" + " defined elsif end ensure false in module next nil not or redo rescue " + "retry self super then true undef unless until when yield BEGIN END "; var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " + "function in local set then until "; var ALL_KEYWORDS = (CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS + PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS); var PR_STRING = 'str'; var PR_KEYWORD = 'kwd'; var PR_COMMENT = 'com'; var PR_TYPE = 'typ'; var PR_LITERAL = 'lit'; var PR_PUNCTUATION = 'pun'; var PR_PLAIN = 'pln'; var PR_TAG = 'tag'; var PR_DECLARATION = 'dec'; var PR_SOURCE = 'src'; var PR_ATTRIB_NAME = 'atn'; var PR_ATTRIB_VALUE = 'atv'; var PR_NOCODE = 'nocode'; function isWordChar(ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); } function spliceArrayInto(inserted, container, containerPosition, countReplaced) { inserted.unshift(containerPosition, countReplaced || 0); try { container.splice.apply(container, inserted); } finally { inserted.splice(0, 2); } } var REGEXP_PRECEDER_PATTERN = function() { var preceders = ["!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=", "&=", "(", "*", "*=", "+=", ",", "-=", "->", "/", "/=", ":", "::", ";", "<", "<<", "<<=", "<=", "=", "==", "===", ">", ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[", "^", "^=", "^^", "^^=", "{", "|", "|=", "||", "||=", "~", "break", "case", "continue", "delete", "do", "else", "finally", "instanceof", "return", "throw", "try", "typeof"]; var pattern = '(?:' + '(?:(?:^|[^0-9.])\\.{1,3})|' + '(?:(?:^|[^\\+])\\+)|' + '(?:(?:^|[^\\-])-)'; for (var i = 0; i < preceders.length; ++i) { var preceder = preceders[i]; if (isWordChar(preceder.charAt(0))) { pattern += '|\\b' + preceder; } else { pattern += '|' + preceder.replace(/([^=<>:&])/g, '\\$1'); } } pattern += '|^)\\s*$'; return new RegExp(pattern); } (); var pr_amp = /&/g; var pr_lt = /</g; var pr_gt = />/g; var pr_quot = /\"/g; function attribToHtml(str) { return str.replace(pr_amp, '&amp;').replace(pr_lt, '&lt;').replace(pr_gt, '&gt;').replace(pr_quot, '&quot;'); } function textToHtml(str) { return str.replace(pr_amp, '&amp;').replace(pr_lt, '&lt;').replace(pr_gt, '&gt;'); } var pr_ltEnt = /&lt;/g; var pr_gtEnt = /&gt;/g; var pr_aposEnt = /&apos;/g; var pr_quotEnt = /&quot;/g; var pr_ampEnt = /&amp;/g; var pr_nbspEnt = /&nbsp;/g; function htmlToText(html) { var pos = html.indexOf('&'); if (pos < 0) { return html; } for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0; ) { var end = html.indexOf(';', pos); if (end >= 0) { var num = html.substring(pos + 3, end); var radix = 10; if (num && num.charAt(0) === 'x') { num = num.substring(1); radix = 16; } var codePoint = parseInt(num, radix); if (!isNaN(codePoint)) { html = (html.substring(0, pos) + String.fromCharCode(codePoint) + html.substring(end + 1)); } } } return html.replace(pr_ltEnt, '<').replace(pr_gtEnt, '>').replace(pr_aposEnt, "'").replace(pr_quotEnt, '"').replace(pr_ampEnt, '&').replace(pr_nbspEnt, ' '); } function isRawContent(node) { return 'XMP' === node.tagName; } function normalizedHtml(node, out) { switch (node.nodeType) { case 1: var name = node.tagName.toLowerCase(); out.push('<', name); for (var i = 0; i < node.attributes.length; ++i) { var attr = node.attributes[i]; if (!attr.specified) { continue; } out.push(' '); normalizedHtml(attr, out); } out.push('>'); for (var child = node.firstChild; child; child = child.nextSibling) { normalizedHtml(child, out); } if (node.firstChild || !/^(?:br|link|img)$/.test(name)) { out.push('<\/', name, '>'); } break; case 2: out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"'); break; case 3: case 4: out.push(textToHtml(node.nodeValue)); break; } } var PR_innerHtmlWorks = null; function getInnerHtml(node) { if (null === PR_innerHtmlWorks) { var testNode = document.createElement('PRE'); testNode.appendChild(document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />')); PR_innerHtmlWorks = !/</.test(testNode.innerHTML); } if (PR_innerHtmlWorks) { var content = node.innerHTML; if (isRawContent(node)) { content = textToHtml(content); } return content; } var out = []; for (var child = node.firstChild; child; child = child.nextSibling) { normalizedHtml(child, out); } return out.join(''); } function makeTabExpander(tabWidth) { var SPACES = '                '; var charInLine = 0; return function(plainText) { var out = null; var pos = 0; for (var i = 0, n = plainText.length; i < n; ++i) { var ch = plainText.charAt(i); switch (ch) { case '\t': if (!out) { out = []; } out.push(plainText.substring(pos, i)); var nSpaces = tabWidth - (charInLine % tabWidth); charInLine += nSpaces; for (; nSpaces >= 0; nSpaces -= SPACES.length) { out.push(SPACES.substring(0, nSpaces)); } pos = i + 1; break; case '\n': charInLine = 0; break; default: ++charInLine; } } if (!out) { return plainText; } out.push(plainText.substring(pos)); return out.join(''); }; } var pr_chunkPattern = /(?:[^<]+|<!--[\s\S]*?-->|<!\[CDATA\[([\s\S]*?)\]\]>|<\/?[a-zA-Z][^>]*>|<)/g; var pr_commentPrefix = /^<!--/; var pr_cdataPrefix = /^<\[CDATA\[/; var pr_brPrefix = /^<br\b/i; var pr_tagNameRe = /^<(\/?)([a-zA-Z]+)/; function extractTags(s) { var matches = s.match(pr_chunkPattern); var sourceBuf = []; var sourceBufLen = 0; var extractedTags = []; if (matches) { for (var i = 0, n = matches.length; i < n; ++i) { var match = matches[i]; if (match.length > 1 && match.charAt(0) === '<') { if (pr_commentPrefix.test(match)) { continue; } if (pr_cdataPrefix.test(match)) { sourceBuf.push(match.substring(9, match.length - 3)); sourceBufLen += match.length - 12; } else if (pr_brPrefix.test(match)) { sourceBuf.push('\n'); ++sourceBufLen; } else { if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) { var name = match.match(pr_tagNameRe)[2]; var depth = 1; end_tag_loop: for (var j = i + 1; j < n; ++j) { var name2 = matches[j].match(pr_tagNameRe); if (name2 && name2[2] === name) { if (name2[1] === '/') { if (--depth === 0) { break end_tag_loop; } } else { ++depth; } } } if (j < n) { extractedTags.push(sourceBufLen, matches.slice(i, j + 1).join('')); i = j; } else { extractedTags.push(sourceBufLen, match); } } else { extractedTags.push(sourceBufLen, match); } } } else { var literalText = htmlToText(match); sourceBuf.push(literalText); sourceBufLen += literalText.length; } } } return { source: sourceBuf.join(''), tags: extractedTags }; } function isNoCodeTag(tag) { return !!tag.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g, ' $1="$2$3$4"').match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/); } function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) { var shortcuts = {}; (function() { var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns); for (var i = allPatterns.length; --i >= 0; ) { var patternParts = allPatterns[i]; var shortcutChars = patternParts[3]; if (shortcutChars) { for (var c = shortcutChars.length; --c >= 0; ) { shortcuts[shortcutChars.charAt(c)] = patternParts; } } } })(); var nPatterns = fallthroughStylePatterns.length; var notWs = /\S/; return function(sourceCode, opt_basePos) { opt_basePos = opt_basePos || 0; var decorations = [opt_basePos, PR_PLAIN]; var lastToken = ''; var pos = 0; var tail = sourceCode; while (tail.length) { var style; var token = null; var match; var patternParts = shortcuts[tail.charAt(0)]; if (patternParts) { match = tail.match(patternParts[1]); token = match[0]; style = patternParts[0]; } else { for (var i = 0; i < nPatterns; ++i) { patternParts = fallthroughStylePatterns[i]; var contextPattern = patternParts[2]; if (contextPattern && !contextPattern.test(lastToken)) { continue; } match = tail.match(patternParts[1]); if (match) { token = match[0]; style = patternParts[0]; break; } } if (!token) { style = PR_PLAIN; token = tail.substring(0, 1); } } decorations.push(opt_basePos + pos, style); pos += token.length; tail = tail.substring(token.length); if (style !== PR_COMMENT && notWs.test(token)) { lastToken = token; } } return decorations; }; } var PR_MARKUP_LEXER = createSimpleLexer([], [[PR_PLAIN, /^[^<]+/, null], [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/, null], [PR_COMMENT, /^<!--[\s\S]*?(?:-->|$)/, null], [PR_SOURCE, /^<\?[\s\S]*?(?:\?>|$)/, null], [PR_SOURCE, /^<%[\s\S]*?(?:%>|$)/, null], [PR_SOURCE, /^<(script|style|xmp)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/i, null], [PR_TAG, /^<\/?\w[^<>]*>/, null]]); var PR_SOURCE_CHUNK_PARTS = /^(<[^>]*>)([\s\S]*)(<\/[^>]*>)$/; function tokenizeMarkup(source) { var decorations = PR_MARKUP_LEXER(source); for (var i = 0; i < decorations.length; i += 2) { if (decorations[i + 1] === PR_SOURCE) { var start, end; start = decorations[i]; end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var sourceChunk = source.substring(start, end); var match = sourceChunk.match(PR_SOURCE_CHUNK_PARTS); if (match) { decorations.splice(i, 2, start, PR_TAG, start + match[1].length, PR_SOURCE, start + match[1].length + (match[2] || '').length, PR_TAG); } } } return decorations; } var PR_TAG_LEXER = createSimpleLexer([[PR_ATTRIB_VALUE, /^\'[^\']*(?:\'|$)/, null, "'"], [PR_ATTRIB_VALUE, /^\"[^\"]*(?:\"|$)/, null, '"'], [PR_PUNCTUATION, /^[<>\/=]+/, null, '<>/=']], [[PR_TAG, /^[\w:\-]+/, /^</], [PR_ATTRIB_VALUE, /^[\w\-]+/, /^=/], [PR_ATTRIB_NAME, /^[\w:\-]+/, null], [PR_PLAIN, /^\s+/, null, ' \t\r\n']]); function splitTagAttributes(source, decorations) { for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; if (style === PR_TAG) { var start, end; start = decorations[i]; end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var chunk = source.substring(start, end); var subDecorations = PR_TAG_LEXER(chunk, start); spliceArrayInto(subDecorations, decorations, i, 2); i += subDecorations.length - 2; } } return decorations; } function sourceDecorator(options) { var shortcutStylePatterns = [], fallthroughStylePatterns = []; if (options.tripleQuotedStrings) { shortcutStylePatterns.push([PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/, null, '\'"']); } else if (options.multiLineStrings) { shortcutStylePatterns.push([PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/, null, '\'"`']); } else { shortcutStylePatterns.push([PR_STRING, /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/, null, '"\'']); } fallthroughStylePatterns.push([PR_PLAIN, /^(?:[^\'\"\`\/\#]+)/, null, ' \r\n']); if (options.hashComments) { shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']); } if (options.cStyleComments) { fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]); fallthroughStylePatterns.push([PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]); } if (options.regexLiterals) { var REGEX_LITERAL = ('^/(?=[^/*])' + '(?:[^/\\x5B\\x5C]' + '|\\x5C[\\s\\S]' + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+' + '(?:/|$)'); fallthroughStylePatterns.push([PR_STRING, new RegExp(REGEX_LITERAL), REGEXP_PRECEDER_PATTERN]); } var keywords = wordSet(options.keywords); options = null; var splitStringAndCommentTokens = createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns); var styleLiteralIdentifierPuncRecognizer = createSimpleLexer([], [[PR_PLAIN, /^\s+/, null, ' \r\n'], [PR_PLAIN, /^[a-z_$@][a-z_$@0-9]*/i, null], [PR_LITERAL, /^0x[a-f0-9]+[a-z]/i, null], [PR_LITERAL, /^(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?[a-z]*/i, null, '123456789'], [PR_PUNCTUATION, /^[^\s\w\.$@]+/, null]]); function splitNonStringNonCommentTokens(source, decorations) { for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; if (style === PR_PLAIN) { var start, end, chunk, subDecs; start = decorations[i]; end = i + 2 < decorations.length ? decorations[i + 2] : source.length; chunk = source.substring(start, end); subDecs = styleLiteralIdentifierPuncRecognizer(chunk, start); for (var j = 0, m = subDecs.length; j < m; j += 2) { var subStyle = subDecs[j + 1]; if (subStyle === PR_PLAIN) { var subStart = subDecs[j]; var subEnd = j + 2 < m ? subDecs[j + 2] : chunk.length; var token = source.substring(subStart, subEnd); if (token === '.') { subDecs[j + 1] = PR_PUNCTUATION; } else if (token in keywords) { subDecs[j + 1] = PR_KEYWORD; } else if (/^@?[A-Z][A-Z$]*[a-z][A-Za-z$]*$/.test(token)) { subDecs[j + 1] = token.charAt(0) === '@' ? PR_LITERAL : PR_TYPE; } } } spliceArrayInto(subDecs, decorations, i, 2); i += subDecs.length - 2; } } return decorations; } return function(sourceCode) { var decorations = splitStringAndCommentTokens(sourceCode); decorations = splitNonStringNonCommentTokens(sourceCode, decorations); return decorations; }; } var decorateSource = sourceDecorator({ keywords: ALL_KEYWORDS, hashComments: true, cStyleComments: true, multiLineStrings: true, regexLiterals: true }); function splitSourceNodes(source, decorations) { for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; if (style === PR_SOURCE) { var start, end; start = decorations[i]; end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var subDecorations = decorateSource(source.substring(start, end)); for (var j = 0, m = subDecorations.length; j < m; j += 2) { subDecorations[j] += start; } spliceArrayInto(subDecorations, decorations, i, 2); i += subDecorations.length - 2; } } return decorations; } function splitSourceAttributes(source, decorations) { var nextValueIsSource = false; for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; var start, end; if (style === PR_ATTRIB_NAME) { start = decorations[i]; end = i + 2 < decorations.length ? decorations[i + 2] : source.length; nextValueIsSource = /^on|^style$/i.test(source.substring(start, end)); } else if (style === PR_ATTRIB_VALUE) { if (nextValueIsSource) { start = decorations[i]; end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var attribValue = source.substring(start, end); var attribLen = attribValue.length; var quoted = (attribLen >= 2 && /^[\"\']/.test(attribValue) && attribValue.charAt(0) === attribValue.charAt(attribLen - 1)); var attribSource; var attribSourceStart; var attribSourceEnd; if (quoted) { attribSourceStart = start + 1; attribSourceEnd = end - 1; attribSource = attribValue; } else { attribSourceStart = start + 1; attribSourceEnd = end - 1; attribSource = attribValue.substring(1, attribValue.length - 1); } var attribSourceDecorations = decorateSource(attribSource); for (var j = 0, m = attribSourceDecorations.length; j < m; j += 2) { attribSourceDecorations[j] += attribSourceStart; } if (quoted) { attribSourceDecorations.push(attribSourceEnd, PR_ATTRIB_VALUE); spliceArrayInto(attribSourceDecorations, decorations, i + 2, 0); } else { spliceArrayInto(attribSourceDecorations, decorations, i, 2); } } nextValueIsSource = false; } } return decorations; } function decorateMarkup(sourceCode) { var decorations = tokenizeMarkup(sourceCode); decorations = splitTagAttributes(sourceCode, decorations); decorations = splitSourceNodes(sourceCode, decorations); decorations = splitSourceAttributes(sourceCode, decorations); return decorations; } function recombineTagsAndDecorations(sourceText, extractedTags, decorations) { var html = []; var outputIdx = 0; var openDecoration = null; var currentDecoration = null; var tagPos = 0; var decPos = 0; var tabExpander = makeTabExpander(PR_TAB_WIDTH); var adjacentSpaceRe = /([\r\n ]) /g; var startOrSpaceRe = /(^| ) /gm; var newlineRe = /\r\n?|\n/g; var trailingSpaceRe = /[ \r\n]$/; var lastWasSpace = true; function emitTextUpTo(sourceIdx) { if (sourceIdx > outputIdx) { if (openDecoration && openDecoration !== currentDecoration) { html.push('</span>'); openDecoration = null; } if (!openDecoration && currentDecoration) { openDecoration = currentDecoration; html.push('<span class="', openDecoration, '">'); } var htmlChunk = textToHtml(tabExpander(sourceText.substring(outputIdx, sourceIdx))).replace(lastWasSpace ? startOrSpaceRe : adjacentSpaceRe, '$1&nbsp;'); lastWasSpace = trailingSpaceRe.test(htmlChunk); html.push(htmlChunk.replace(newlineRe, '<br />')); outputIdx = sourceIdx; } } while (true) { var outputTag; if (tagPos < extractedTags.length) { if (decPos < decorations.length) { outputTag = extractedTags[tagPos] <= decorations[decPos]; } else { outputTag = true; } } else { outputTag = false; } if (outputTag) { emitTextUpTo(extractedTags[tagPos]); if (openDecoration) { html.push('</span>'); openDecoration = null; } html.push(extractedTags[tagPos + 1]); tagPos += 2; } else if (decPos < decorations.length) { emitTextUpTo(decorations[decPos]); currentDecoration = decorations[decPos + 1]; decPos += 2; } else { break; } } emitTextUpTo(sourceText.length); if (openDecoration) { html.push('</span>'); } return html.join(''); } var langHandlerRegistry = {}; function registerLangHandler(handler, fileExtensions) { for (var i = fileExtensions.length; --i >= 0; ) { var ext = fileExtensions[i]; if (!langHandlerRegistry.hasOwnProperty(ext)) { langHandlerRegistry[ext] = handler; } else if ('console' in window) { console.log('cannot override language handler %s', ext); } } } registerLangHandler(decorateSource, ['default-code']); registerLangHandler(decorateMarkup, ['default-markup', 'html', 'htm', 'xhtml', 'xml', 'xsl']); registerLangHandler(sourceDecorator({ keywords: CPP_KEYWORDS, hashComments: true, cStyleComments: true }), ['c', 'cc', 'cpp', 'cs', 'cxx', 'cyc']); registerLangHandler(sourceDecorator({ keywords: JAVA_KEYWORDS, cStyleComments: true }), ['java']); registerLangHandler(sourceDecorator({ keywords: SH_KEYWORDS, hashComments: true, multiLineStrings: true }), ['bsh', 'csh', 'sh']); registerLangHandler(sourceDecorator({ keywords: PYTHON_KEYWORDS, hashComments: true, multiLineStrings: true, tripleQuotedStrings: true }), ['cv', 'py']); registerLangHandler(sourceDecorator({ keywords: PERL_KEYWORDS, hashComments: true, multiLineStrings: true, regexLiterals: true }), ['perl', 'pl', 'pm']); registerLangHandler(sourceDecorator({ keywords: RUBY_KEYWORDS, hashComments: true, multiLineStrings: true, regexLiterals: true }), ['rb']); registerLangHandler(sourceDecorator({ keywords: JSCRIPT_KEYWORDS, cStyleComments: true, regexLiterals: true }), ['js']); function prettyPrintOne(sourceCodeHtml, opt_langExtension) { try { var sourceAndExtractedTags = extractTags(sourceCodeHtml); var source = sourceAndExtractedTags.source; var extractedTags = sourceAndExtractedTags.tags; if (!langHandlerRegistry.hasOwnProperty(opt_langExtension)) { opt_langExtension = /^\s*</.test(source) ? 'default-markup' : 'default-code'; } var decorations = langHandlerRegistry[opt_langExtension].call({}, source); return recombineTagsAndDecorations(source, extractedTags, decorations); } catch (e) { if ('console' in window) { console.log(e); console.trace(); } return sourceCodeHtml; } } function prettyPrint(opt_whenDone) { var isIE6 = _pr_isIE6(); var codeSegments = [document.getElementsByTagName('pre'), document.getElementsByTagName('code'), document.getElementsByTagName('xmp')]; var elements = []; for (var i = 0; i < codeSegments.length; ++i) { for (var j = 0; j < codeSegments[i].length; ++j) { elements.push(codeSegments[i][j]); } } codeSegments = null; var k = 0; function doWork() { var endTime = (PR_SHOULD_USE_CONTINUATION ? new Date().getTime() + 250 : Infinity); for (; k < elements.length && new Date().getTime() < endTime; k++) { var cs = elements[k]; if (cs.className && cs.className.indexOf('prettyprint') >= 0) { var langExtension = cs.className.match(/\blang-(\w+)\b/); if (langExtension) { langExtension = langExtension[1]; } var nested = false; for (var p = cs.parentNode; p; p = p.parentNode) { if ((p.tagName === 'pre' || p.tagName === 'code' || p.tagName === 'xmp') && p.className && p.className.indexOf('prettyprint') >= 0) { nested = true; break; } } if (!nested) { var content = getInnerHtml(cs); content = content.replace(/(?:\r\n?|\n)$/, ''); var newContent = prettyPrintOne(content, langExtension); if (!isRawContent(cs)) { cs.innerHTML = newContent; } else { var pre = document.createElement('PRE'); for (var i = 0; i < cs.attributes.length; ++i) { var a = cs.attributes[i]; if (a.specified) { var aname = a.name.toLowerCase(); if (aname === 'class') { pre.className = a.value; } else { pre.setAttribute(a.name, a.value); } } } pre.innerHTML = newContent; cs.parentNode.replaceChild(pre, cs); cs = pre; } if (isIE6 && cs.tagName === 'PRE') { var lineBreaks = cs.getElementsByTagName('br'); for (var j = lineBreaks.length; --j >= 0; ) { var lineBreak = lineBreaks[j]; lineBreak.parentNode.replaceChild(document.createTextNode('\r\n'), lineBreak); } } } } } if (k < elements.length) { setTimeout(doWork, 250); } else if (opt_whenDone) { opt_whenDone(); } } doWork(); } window['PR_normalizedHtml'] = normalizedHtml; window['prettyPrintOne'] = prettyPrintOne; window['prettyPrint'] = prettyPrint; window['PR'] = { 'createSimpleLexer': createSimpleLexer, 'registerLangHandler': registerLangHandler, 'sourceDecorator': sourceDecorator, 'PR_ATTRIB_NAME': PR_ATTRIB_NAME, 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE, 'PR_COMMENT': PR_COMMENT, 'PR_DECLARATION': PR_DECLARATION, 'PR_KEYWORD': PR_KEYWORD, 'PR_LITERAL': PR_LITERAL, 'PR_NOCODE': PR_NOCODE, 'PR_PLAIN': PR_PLAIN, 'PR_PUNCTUATION': PR_PUNCTUATION, 'PR_SOURCE': PR_SOURCE, 'PR_STRING': PR_STRING, 'PR_TAG': PR_TAG, 'PR_TYPE': PR_TYPE }; })();