Completed
Push — master ( 42b80d...9c8981 )
by Markus
04:20
created

util.js (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
// util.js
2
// copyright Sébastien Lucas
3
// https://github.com/seblucas/cops
4
5
/*jshint curly: true, latedef: true, trailing: true, noarg: true, undef: true, browser: true, jquery: true, unused: true, devel: true, loopfunc: true */
6
/*global LRUCache, doT, Bloodhound, postRefresh */
7
8
/** global: navigator */
9
/** global: history */
10
11
var templatePage, templateBookDetail, templateMain, templateSuggestion, currentData, before, filterList;
12
13
if (typeof LRUCache != 'undefined') {
14
    var cache = new LRUCache(30);
15
}
16
17
$.ajaxSetup({
18
    cache: false
19
});
20
21
var copsTypeahead = new Bloodhound({
22
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
23
    queryTokenizer: Bloodhound.tokenizers.whitespace,
24
    limit: 30,
25
    remote: {
26
                url: 'getJSON.php?page=9&search=1&db=%DB&query=%QUERY',
27
                replace: function (url, query) {
28
                    if (currentData.multipleDatabase === 1 && currentData.databaseId === "") {
29
                        return url.replace('%QUERY', query).replace('&db=%DB', "");
30
                    }
31
                    return url.replace('%QUERY', query).replace('%DB', currentData.databaseId);
32
                }
33
            }
34
});
35
36
copsTypeahead.initialize();
37
38
var DEBUG = false;
39
var isPushStateEnabled = window.history && window.history.pushState && window.history.replaceState &&
40
  // pushState isn't reliable on iOS until 5.
41
  !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/);
42
43
function debug_log(text) {
44
    if ( DEBUG ) {
45
        console.log(text);
46
    }
47
}
48
49
/*exported updateCookie */
50
function updateCookie (id) {
51
    if ($(id).prop('pattern') && !$(id).val().match(new RegExp ($(id).prop('pattern')))) {
52
        return;
53
    }
54
    var name = $(id).attr('id');
55
    var value = $(id).val ();
56
    $.cookie(name, value, { expires: 365 });
57
}
58
59
/*exported updateCookieFromCheckbox */
60
function updateCookieFromCheckbox (id) {
61
    var name = $(id).attr('id');
62
    if ((/^style/).test (name)) {
63
        name = "style";
64
    }
65
    if ($(id).is(":checked"))
66
    {
67
        if ($(id).is(':radio')) {
68
            $.cookie(name, $(id).val (), { expires: 365 });
69
        } else {
70
            $.cookie(name, '1', { expires: 365 });
71
        }
72
    }
73
    else
74
    {
75
        $.cookie(name, '0', { expires: 365 });
76
    }
77
}
78
79
/*exported updateCookieFromCheckboxGroup */
80
function updateCookieFromCheckboxGroup (id) {
81
    var name = $(id).attr('name');
82
    var idBase = name.replace (/\[\]/, "");
83
    var group = [];
84
    $(':checkbox[name="' + name + '"]:checked').each (function () {
85
        var id = $(this).attr("id");
86
        group.push (id.replace (idBase + "_", ""));
87
    });
88
    $.cookie(idBase, group.join (), { expires: 365 });
89
}
90
91
92
function elapsed () {
93
    var elapsedTime = new Date () - before;
94
    return "Elapsed : " + elapsedTime;
95
}
96
97
function retourMail(data) {
98
    $("#mailButton :first-child").removeClass ("icon-spinner icon-spin").addClass ("icon-envelope");
99
    alert (data);
100
}
101
102
/*exported sendToMailAddress */
103
function sendToMailAddress (component, dataid) {
104
    var email = $.cookie ('email');
105
    if (!$.cookie ('email')) {
106
        email = window.prompt (currentData.c.i18n.customizeEmail, "");
107
        if (email === null)
108
        {
109
            return;
110
        }
111
        $.cookie ('email', email, { expires: 365 });
112
    }
113
    var url = 'sendtomail.php';
114
    if (currentData.databaseId) {
115
        url = url + '?db=' + currentData.databaseId;
116
    }
117
    $("#mailButton :first-child").removeClass ("icon-envelope").addClass ("icon-spinner icon-spin");
118
    $.ajax ({'url': url, 'type': 'post', 'data': { 'data':  dataid, 'email': email }, 'success': retourMail});
119
}
120
121
function str_format () {
122
    var s = arguments[0];
123
    for (var i = 0; i < arguments.length - 1; i++) {
124
        var reg = new RegExp("\\{" + i + "\\}", "gm");
125
        s = s.replace(reg, arguments[i + 1]);
126
    }
127
    return s;
128
}
129
130
function isDefined(x) {
131
    var undefinedVar;
132
    return x !== undefinedVar;
133
}
134
135
function getCurrentOption (option) {
136
    if (!$.cookie (option)) {
137
        if (currentData && currentData.c && currentData.c.config && currentData.c.config [option]) {
138
            return currentData.c.config [option];
139
        }
140
    }
141
    return $.cookie (option);
142
}
143
144
/*exported htmlspecialchars */
145
function htmlspecialchars(str) {
146
    return String(str)
147
            .replace(/&/g, '&amp;')
148
            .replace(/"/g, '&quot;')
149
            .replace(/'/g, '&#39;')
150
            .replace(/</g, '&lt;')
151
            .replace(/>/g, '&gt;');
152
}
153
154
/************************************************
155
 * All functions needed to filter the book list by tags
156
 ************************************************
157
 */
158
159
function getTagList () {
160
    var tagList = {};
161
    $(".se").each (function(){
162
        if ($(this).parents (".filtered").length > 0) { return; }
163
        var taglist = $(this).text();
164
165
        var tagarray = taglist.split (",");
166
        for (var i in tagarray) {
167
            if (!tagarray.hasOwnProperty(i)) {
168
                continue;
169
            }
170
            var tag = tagarray [i].replace(/^\s+/g,'').replace(/\s+$/g,'');
171
            tagList [tag] = 1;
172
        }
173
    });
174
    return tagList;
175
}
176
177
function updateFilters () {
178
    var tagList = getTagList ();
179
180
    // If there is already some filters then let's prepare to update the list
181
    $("#filter ul li").each (function () {
182
        var text = $(this).text ();
183
        if (isDefined (tagList [text]) || $(this).attr ('class')) {
184
            tagList [text] = 0;
185
        } else {
186
            tagList [text] = -1;
187
        }
188
    });
189
190
    // Update the filter -1 to remove, 1 to add, 0 already there
191
    for (var tag in tagList) {
192
        if (!tagList.hasOwnProperty(tag)) {
193
            continue;
194
        }
195
        var tagValue = tagList [tag];
196
        if (tagValue === -1) {
197
            $("#filter ul li").filter (function () { return $.text([this]) === tag; }).remove();
198
        }
199
        if (tagValue === 1) {
200
            $("#filter ul").append ("<li>" + tag + "</li>");
201
        }
202
    }
203
204
    $("#filter ul").append ("<li>_CLEAR_</li>");
205
206
    // Sort the list alphabetically
207
    $('#filter ul li').sortElements(function(a, b){
208
        return $(a).text() > $(b).text() ? 1 : -1;
209
    });
210
}
211
212
function doFilter () {
213
    $(".books").removeClass("filtered");
214
    if (jQuery.isEmptyObject(filterList)) {
215
        updateFilters ();
216
        return;
217
    }
218
219
    $(".se").each (function(){
220
        var taglist = ", " + $(this).text() + ", ";
221
        var toBeFiltered = false;
222
        for (var filter in filterList) {
0 ignored issues
show
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
223
            var onlyThisTag = filterList [filter];
224
            filter = ', ' + filter + ', ';
225
            var myreg = new RegExp (filter);
226
            if (myreg.test (taglist)) {
227
                if (onlyThisTag === false) {
228
                    toBeFiltered = true;
229
                }
230
            } else {
231
                if (onlyThisTag === true) {
232
                    toBeFiltered = true;
233
                }
234
            }
235
        }
236
        if (toBeFiltered) { $(this).parents (".books").addClass ("filtered"); }
237
    });
238
239
    // Handle the books with no tags
240
    var atLeastOneTagSelected = false;
241
    for (var filter in filterList) {
242
        if (!filterList.hasOwnProperty(filter)) {
243
            continue;
244
        }
245
        if (filterList[filter] === true) {
246
            atLeastOneTagSelected = true;
247
        }
248
    }
249
    if (atLeastOneTagSelected) {
250
        $(".books").not (":has(span.se)").addClass ("filtered");
251
    }
252
253
    updateFilters ();
254
}
255
256
function handleFilterEvents () {
257
    $("#filter ul").on ("click", "li", function(){
258
        var filter = $(this).text ();
259
        if (filter === "_CLEAR_") {
260
            filterList = {};
261
            $("#filter ul li").removeClass ("filter-exclude");
262
            $("#filter ul li").removeClass ("filter-include");
263
            doFilter ();
264
            return;
265
        }
266
        switch ($(this).attr("class")) {
267
            case "filter-include" :
268
                $(this).attr("class", "filter-exclude");
269
                filterList [filter] = false;
270
                break;
271
            case "filter-exclude" :
272
                $(this).removeClass ("filter-exclude");
273
                delete filterList [filter];
274
                break;
275
            default :
276
                $(this).attr("class", "filter-include");
277
                filterList [filter] = true;
278
                break;
279
        }
280
        doFilter ();
281
    });
282
}
283
284
/************************************************
285
 * Functions to handle Ajax navigation
286
 ************************************************
287
 */
288
289
var updatePage, navigateTo;
290
291
updatePage = function (data) {
292
    var result;
293
    filterList = {};
294
    data.c = currentData.c;
295
    if (false && $("section").length && currentData.isPaginated === 0 &&  data.isPaginated === 0) {
296
        // Partial update (for now disabled)
297
        debug_log ("Partial update");
298
        result = templateMain (data);
299
        $("h1").html (data.title);
300
        $("section").html (result);
301
    } else {
302
        // Full update
303
        result = templatePage (data);
304
        $("body").html (result);
305
    }
306
    document.title = data.title;
307
    currentData = data;
308
    setTimeout( function() { $("input[name=query]").focus(); }, 500 );
309
310
    debug_log (elapsed ());
311
312
    if ($.cookie('toolbar') === '1') { $("#tool").show (); }
313
    if (currentData.containsBook === 1) {
314
        $("#sortForm").show ();
315
        if (getCurrentOption ("html_tag_filter") === "1") {
316
            $("#filter ul").empty ();
317
            updateFilters ();
318
            handleFilterEvents ();
319
        }
320
    } else {
321
        $("#sortForm").hide ();
322
    }
323
324
    $('input[name=query]').typeahead(
325
    {
326
        hint: true,
327
        minLength : 3
328
    },
329
    {
330
        name: 'search',
331
        displayKey: 'title',
332
        templates: {
333
            suggestion: templateSuggestion
334
        },
335
        source: copsTypeahead.ttAdapter()
336
    });
337
338
    $('input[name=query]').bind('typeahead:selected', function(obj, datum) {
339
        if (isPushStateEnabled) {
340
            navigateTo (datum.navlink);
341
        } else {
342
            window.location = datum.navlink;
343
        }
344
    });
345
346
    if(typeof postRefresh == 'function')
347
    { postRefresh(); }
348
};
349
350
navigateTo = function (url) {
351
    $("h1").append (" <i class='icon-spinner icon-spin'></i>");
352
    before = new Date ();
353
    var jsonurl = url.replace ("index", "getJSON");
354
    var cachedData = cache.get (jsonurl);
0 ignored issues
show
The variable cache does not seem to be initialized in case typeof LRUCache != "undefined" on line 13 is false. Are you sure this can never be the case?
Loading history...
355
    if (cachedData) {
356
        history.pushState(jsonurl, "", url);
357
        updatePage (cachedData);
358
    } else {
359
        $.getJSON(jsonurl, function(data) {
360
            history.pushState(jsonurl, "", url);
361
            cache.put (jsonurl, data);
0 ignored issues
show
The variable cache does not seem to be initialized in case typeof LRUCache != "undefined" on line 13 is false. Are you sure this can never be the case?
Loading history...
362
            updatePage (data);
363
        });
364
    }
365
};
366
367
function link_Clicked (event) {
368
    var currentLink = $(this);
369
    if (!isPushStateEnabled ||
370
        currentData.page === "19") {
371
        return;
372
    }
373
    event.preventDefault();
374
    var url = currentLink.attr('href');
375
376
    if ($(".mfp-ready").length)
377
    {
378
        $.magnificPopup.close();
379
    }
380
381
    // The bookdetail / about should be displayed in a lightbox
382
    if (getCurrentOption ("use_fancyapps") === "1" &&
383
        (currentLink.hasClass ("fancydetail") || currentLink.hasClass ("fancyabout"))) {
384
        before = new Date ();
385
        var jsonurl = url.replace ("index", "getJSON");
386
        $.getJSON(jsonurl, function(data) {
387
            data.c = currentData.c;
388
            var detail = "";
389
            if (data.page === "16") {
390
                detail = data.fullhtml;
391
            } else {
392
                detail = templateBookDetail (data);
393
            }
394
            $.magnificPopup.open({
395
              items: {
396
                src: detail,
397
                type: 'inline'
398
              }
399
            });
400
            debug_log (elapsed ());
401
        });
402
        return;
403
    }
404
    navigateTo (url);
405
}
406
407
function search_Submitted (event) {
408
    if (!isPushStateEnabled ||
409
        currentData.page === "19") {
410
        return;
411
    }
412
    event.preventDefault();
413
    var url = str_format ("index.php?page=9&current={0}&query={1}&db={2}", currentData.page, encodeURIComponent ($("input[name=query]").val ()), currentData.databaseId);
414
    navigateTo (url);
415
}
416
417
/*exported handleLinks */
418
function handleLinks () {
419
    $("body").on ("click", "a[href^='index']", link_Clicked);
420
    $("body").on ("submit", "#searchForm", search_Submitted);
421
    $("body").on ("click", "#sort", function(){
422
        $('.books').sortElements(function(a, b){
423
            var test = 1;
424
            if ($("#sortorder").val() === "desc")
425
            {
426
                test = -1;
427
            }
428
            return $(a).find ("." + $("#sortchoice").val()).text() > $(b).find ("." + $("#sortchoice").val()).text() ? test : -test;
429
        });
430
    });
431
432
    $("body").on ("click", ".headright", function(){
433
        if ($("#tool").is(":hidden")) {
434
            $("#tool").slideDown("slow");
435
            $("input[name=query]").focus();
436
            $.cookie('toolbar', '1', { expires: 365 });
437
        } else {
438
            $("#tool").slideUp();
439
            $.removeCookie('toolbar');
440
        }
441
    });
442
    $("body").magnificPopup({
443
        delegate: '.fancycover', // child items selector, by clicking on it popup will open
444
        type: 'image',
445
        gallery:{enabled:true, preload: [0,2]},
446
        disableOn: function() {
447
          if( getCurrentOption ("use_fancyapps") === "1" ) {
448
            return true;
449
          }
450
          return false;
451
        }
452
    });
453
}
454
455
window.onpopstate = function(event) {
456
    if (!isDefined (currentData)) {
457
        return;
458
    }
459
460
    before = new Date ();
461
    var data = cache.get (event.state);
0 ignored issues
show
The variable cache does not seem to be initialized in case typeof LRUCache != "undefined" on line 13 is false. Are you sure this can never be the case?
Loading history...
462
    updatePage (data);
463
};
464
465
$(document).keydown(function(e){
466
    if (e.keyCode === 37 && $("#prevLink").length > 0) {
467
        navigateTo ($("#prevLink").attr('href'));
468
    }
469
    if (e.keyCode === 39  && $("#nextLink").length > 0) {
470
        navigateTo ($("#nextLink").attr('href'));
471
    }
472
});
473
474
/*exported initiateAjax */
475
function initiateAjax (url, theme) {
476
    $.when($.get('templates/' + theme + '/header.html'),
477
           $.get('templates/' + theme + '/footer.html'),
478
           $.get('templates/' + theme + '/bookdetail.html'),
479
           $.get('templates/' + theme + '/main.html'),
480
           $.get('templates/' + theme + '/page.html'),
481
           $.get('templates/' + theme + '/suggestion.html'),
482
           $.getJSON(url)).done(function(header, footer, bookdetail, main, page, suggestion, data){
483
        templateBookDetail = doT.template (bookdetail [0]);
484
485
        var defMain = {
486
            bookdetail: bookdetail [0]
487
        };
488
489
        templateMain = doT.template (main [0], undefined, defMain);
490
491
        var defPage = {
492
            header: header [0],
493
            footer: footer [0],
494
            main  : main [0],
495
            bookdetail: bookdetail [0]
496
        };
497
498
        templatePage = doT.template (page [0], undefined, defPage);
499
500
        templateSuggestion = doT.template (suggestion [0]);
501
502
        currentData = data [0];
503
504
        updatePage (data [0]);
505
        cache.put (url, data [0]);
0 ignored issues
show
The variable cache does not seem to be initialized in case typeof LRUCache != "undefined" on line 13 is false. Are you sure this can never be the case?
Loading history...
506
        if (isPushStateEnabled) {
507
            history.replaceState(url, "", window.location);
508
        }
509
        handleLinks ();
510
    });
511
}
512