Completed
Push — master ( 92ab4e...42b80d )
by Markus
04:22
created

util.js (2 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, history */
9
10
var templatePage, templateBookDetail, templateMain, templateSuggestion, currentData, before, filterList;
11
12
if (typeof LRUCache != 'undefined') {
13
    var cache = new LRUCache(30);
14
}
15
16
$.ajaxSetup({
17
    cache: false
18
});
19
20
var copsTypeahead = new Bloodhound({
21
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
22
    queryTokenizer: Bloodhound.tokenizers.whitespace,
23
    limit: 30,
24
    remote: {
25
                url: 'getJSON.php?page=9&search=1&db=%DB&query=%QUERY',
26
                replace: function (url, query) {
27
                    if (currentData.multipleDatabase === 1 && currentData.databaseId === "") {
28
                        return url.replace('%QUERY', query).replace('&db=%DB', "");
29
                    }
30
                    return url.replace('%QUERY', query).replace('%DB', currentData.databaseId);
31
                }
32
            }
33
});
34
35
copsTypeahead.initialize();
36
37
var DEBUG = false;
38
var isPushStateEnabled = window.history && window.history.pushState && window.history.replaceState &&
39
  // pushState isn't reliable on iOS until 5.
40
  !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/);
41
42
function debug_log(text) {
43
    if ( DEBUG ) {
44
        console.log(text);
45
    }
46
}
47
48
/*exported updateCookie */
49
function updateCookie (id) {
50
    if ($(id).prop('pattern') && !$(id).val().match(new RegExp ($(id).prop('pattern')))) {
51
        return;
52
    }
53
    var name = $(id).attr('id');
54
    var value = $(id).val ();
55
    $.cookie(name, value, { expires: 365 });
56
}
57
58
/*exported updateCookieFromCheckbox */
59
function updateCookieFromCheckbox (id) {
60
    var name = $(id).attr('id');
61
    if ((/^style/).test (name)) {
62
        name = "style";
63
    }
64
    if ($(id).is(":checked"))
65
    {
66
        if ($(id).is(':radio')) {
67
            $.cookie(name, $(id).val (), { expires: 365 });
68
        } else {
69
            $.cookie(name, '1', { expires: 365 });
70
        }
71
    }
72
    else
73
    {
74
        $.cookie(name, '0', { expires: 365 });
75
    }
76
}
77
78
/*exported updateCookieFromCheckboxGroup */
79
function updateCookieFromCheckboxGroup (id) {
80
    var name = $(id).attr('name');
81
    var idBase = name.replace (/\[\]/, "");
82
    var group = [];
83
    $(':checkbox[name="' + name + '"]:checked').each (function () {
84
        var id = $(this).attr("id");
85
        group.push (id.replace (idBase + "_", ""));
86
    });
87
    $.cookie(idBase, group.join (), { expires: 365 });
88
}
89
90
91
function elapsed () {
92
    var elapsedTime = new Date () - before;
93
    return "Elapsed : " + elapsedTime;
94
}
95
96
function retourMail(data) {
97
    $("#mailButton :first-child").removeClass ("icon-spinner icon-spin").addClass ("icon-envelope");
98
    alert (data);
99
}
100
101
/*exported sendToMailAddress */
102
function sendToMailAddress (component, dataid) {
103
    var email = $.cookie ('email');
104
    if (!$.cookie ('email')) {
105
        email = window.prompt (currentData.c.i18n.customizeEmail, "");
106
        if (email === null)
107
        {
108
            return;
109
        }
110
        $.cookie ('email', email, { expires: 365 });
111
    }
112
    var url = 'sendtomail.php';
113
    if (currentData.databaseId) {
114
        url = url + '?db=' + currentData.databaseId;
115
    }
116
    $("#mailButton :first-child").removeClass ("icon-envelope").addClass ("icon-spinner icon-spin");
117
    $.ajax ({'url': url, 'type': 'post', 'data': { 'data':  dataid, 'email': email }, 'success': retourMail});
118
}
119
120
function str_format () {
121
    var s = arguments[0];
122
    for (var i = 0; i < arguments.length - 1; i++) {
123
        var reg = new RegExp("\\{" + i + "\\}", "gm");
124
        s = s.replace(reg, arguments[i + 1]);
125
    }
126
    return s;
127
}
128
129
function isDefined(x) {
130
    var undefinedVar;
131
    return x !== undefinedVar;
132
}
133
134
function getCurrentOption (option) {
135
    if (!$.cookie (option)) {
136
        if (currentData && currentData.c && currentData.c.config && currentData.c.config [option]) {
137
            return currentData.c.config [option];
138
        }
139
    }
140
    return $.cookie (option);
141
}
142
143
/*exported htmlspecialchars */
144
function htmlspecialchars(str) {
145
    return String(str)
146
            .replace(/&/g, '&amp;')
147
            .replace(/"/g, '&quot;')
148
            .replace(/'/g, '&#39;')
149
            .replace(/</g, '&lt;')
150
            .replace(/>/g, '&gt;');
151
}
152
153
/************************************************
154
 * All functions needed to filter the book list by tags
155
 ************************************************
156
 */
157
158
function getTagList () {
159
    var tagList = {};
160
    $(".se").each (function(){
161
        if ($(this).parents (".filtered").length > 0) { return; }
162
        var taglist = $(this).text();
163
164
        var tagarray = taglist.split (",");
165
        for (var i in tagarray) {
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...
166
            var tag = tagarray [i].replace(/^\s+/g,'').replace(/\s+$/g,'');
167
            tagList [tag] = 1;
168
        }
169
    });
170
    return tagList;
171
}
172
173
function updateFilters () {
174
    var tagList = getTagList ();
175
176
    // If there is already some filters then let's prepare to update the list
177
    $("#filter ul li").each (function () {
178
        var text = $(this).text ();
179
        if (isDefined (tagList [text]) || $(this).attr ('class')) {
180
            tagList [text] = 0;
181
        } else {
182
            tagList [text] = -1;
183
        }
184
    });
185
186
    // Update the filter -1 to remove, 1 to add, 0 already there
187
    for (var tag in tagList) {
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...
188
        var tagValue = tagList [tag];
189
        if (tagValue === -1) {
190
            $("#filter ul li").filter (function () { return $.text([this]) === tag; }).remove();
191
        }
192
        if (tagValue === 1) {
193
            $("#filter ul").append ("<li>" + tag + "</li>");
194
        }
195
    }
196
197
    $("#filter ul").append ("<li>_CLEAR_</li>");
198
199
    // Sort the list alphabetically
200
    $('#filter ul li').sortElements(function(a, b){
201
        return $(a).text() > $(b).text() ? 1 : -1;
202
    });
203
}
204
205
function doFilter () {
206
    $(".books").removeClass("filtered");
207
    if (jQuery.isEmptyObject(filterList)) {
208
        updateFilters ();
209
        return;
210
    }
211
212
    $(".se").each (function(){
213
        var taglist = ", " + $(this).text() + ", ";
214
        var toBeFiltered = false;
215
        for (var filter in filterList) {
216
            var onlyThisTag = filterList [filter];
217
            filter = ', ' + filter + ', ';
218
            var myreg = new RegExp (filter);
219
            if (myreg.test (taglist)) {
220
                if (onlyThisTag === false) {
221
                    toBeFiltered = true;
222
                }
223
            } else {
224
                if (onlyThisTag === true) {
225
                    toBeFiltered = true;
226
                }
227
            }
228
        }
229
        if (toBeFiltered) { $(this).parents (".books").addClass ("filtered"); }
230
    });
231
232
    // Handle the books with no tags
233
    var atLeastOneTagSelected = false;
234
    for (var filter in filterList) {
235
        if (filterList [filter] === true) {
236
            atLeastOneTagSelected = true;
237
        }
238
    }
239
    if (atLeastOneTagSelected) {
240
        $(".books").not (":has(span.se)").addClass ("filtered");
241
    }
242
243
    updateFilters ();
244
}
245
246
function handleFilterEvents () {
247
    $("#filter ul").on ("click", "li", function(){
248
        var filter = $(this).text ();
249
        if (filter === "_CLEAR_") {
250
            filterList = {};
251
            $("#filter ul li").removeClass ("filter-exclude");
252
            $("#filter ul li").removeClass ("filter-include");
253
            doFilter ();
254
            return;
255
        }
256
        switch ($(this).attr("class")) {
257
            case "filter-include" :
258
                $(this).attr("class", "filter-exclude");
259
                filterList [filter] = false;
260
                break;
261
            case "filter-exclude" :
262
                $(this).removeClass ("filter-exclude");
263
                delete filterList [filter];
264
                break;
265
            default :
266
                $(this).attr("class", "filter-include");
267
                filterList [filter] = true;
268
                break;
269
        }
270
        doFilter ();
271
    });
272
}
273
274
/************************************************
275
 * Functions to handle Ajax navigation
276
 ************************************************
277
 */
278
279
var updatePage, navigateTo;
280
281
updatePage = function (data) {
282
    var result;
283
    filterList = {};
284
    data.c = currentData.c;
285
    if (false && $("section").length && currentData.isPaginated === 0 &&  data.isPaginated === 0) {
286
        // Partial update (for now disabled)
287
        debug_log ("Partial update");
288
        result = templateMain (data);
289
        $("h1").html (data.title);
290
        $("section").html (result);
291
    } else {
292
        // Full update
293
        result = templatePage (data);
294
        $("body").html (result);
295
    }
296
    document.title = data.title;
297
    currentData = data;
298
    setTimeout( function() { $("input[name=query]").focus(); }, 500 );
299
300
    debug_log (elapsed ());
301
302
    if ($.cookie('toolbar') === '1') { $("#tool").show (); }
303
    if (currentData.containsBook === 1) {
304
        $("#sortForm").show ();
305
        if (getCurrentOption ("html_tag_filter") === "1") {
306
            $("#filter ul").empty ();
307
            updateFilters ();
308
            handleFilterEvents ();
309
        }
310
    } else {
311
        $("#sortForm").hide ();
312
    }
313
314
    $('input[name=query]').typeahead(
315
    {
316
        hint: true,
317
        minLength : 3
318
    },
319
    {
320
        name: 'search',
321
        displayKey: 'title',
322
        templates: {
323
            suggestion: templateSuggestion
324
        },
325
        source: copsTypeahead.ttAdapter()
326
    });
327
328
    $('input[name=query]').bind('typeahead:selected', function(obj, datum) {
329
        if (isPushStateEnabled) {
330
            navigateTo (datum.navlink);
331
        } else {
332
            window.location = datum.navlink;
333
        }
334
    });
335
336
    if(typeof postRefresh == 'function')
337
    { postRefresh(); }
338
};
339
340
navigateTo = function (url) {
341
    $("h1").append (" <i class='icon-spinner icon-spin'></i>");
342
    before = new Date ();
343
    var jsonurl = url.replace ("index", "getJSON");
344
    var cachedData = cache.get (jsonurl);
345
    if (cachedData) {
346
        history.pushState(jsonurl, "", url);
347
        updatePage (cachedData);
348
    } else {
349
        $.getJSON(jsonurl, function(data) {
350
            history.pushState(jsonurl, "", url);
351
            cache.put (jsonurl, data);
352
            updatePage (data);
353
        });
354
    }
355
};
356
357
function link_Clicked (event) {
358
    var currentLink = $(this);
359
    if (!isPushStateEnabled ||
360
        currentData.page === "19") {
361
        return;
362
    }
363
    event.preventDefault();
364
    var url = currentLink.attr('href');
365
366
    if ($(".mfp-ready").length)
367
    {
368
        $.magnificPopup.close();
369
    }
370
371
    // The bookdetail / about should be displayed in a lightbox
372
    if (getCurrentOption ("use_fancyapps") === "1" &&
373
        (currentLink.hasClass ("fancydetail") || currentLink.hasClass ("fancyabout"))) {
374
        before = new Date ();
375
        var jsonurl = url.replace ("index", "getJSON");
376
        $.getJSON(jsonurl, function(data) {
377
            data.c = currentData.c;
378
            var detail = "";
379
            if (data.page === "16") {
380
                detail = data.fullhtml;
381
            } else {
382
                detail = templateBookDetail (data);
383
            }
384
            $.magnificPopup.open({
385
              items: {
386
                src: detail,
387
                type: 'inline'
388
              }
389
            });
390
            debug_log (elapsed ());
391
        });
392
        return;
393
    }
394
    navigateTo (url);
395
}
396
397
function search_Submitted (event) {
398
    if (!isPushStateEnabled ||
399
        currentData.page === "19") {
400
        return;
401
    }
402
    event.preventDefault();
403
    var url = str_format ("index.php?page=9&current={0}&query={1}&db={2}", currentData.page, encodeURIComponent ($("input[name=query]").val ()), currentData.databaseId);
404
    navigateTo (url);
405
}
406
407
/*exported handleLinks */
408
function handleLinks () {
409
    $("body").on ("click", "a[href^='index']", link_Clicked);
410
    $("body").on ("submit", "#searchForm", search_Submitted);
411
    $("body").on ("click", "#sort", function(){
412
        $('.books').sortElements(function(a, b){
413
            var test = 1;
414
            if ($("#sortorder").val() === "desc")
415
            {
416
                test = -1;
417
            }
418
            return $(a).find ("." + $("#sortchoice").val()).text() > $(b).find ("." + $("#sortchoice").val()).text() ? test : -test;
419
        });
420
    });
421
422
    $("body").on ("click", ".headright", function(){
423
        if ($("#tool").is(":hidden")) {
424
            $("#tool").slideDown("slow");
425
            $("input[name=query]").focus();
426
            $.cookie('toolbar', '1', { expires: 365 });
427
        } else {
428
            $("#tool").slideUp();
429
            $.removeCookie('toolbar');
430
        }
431
    });
432
    $("body").magnificPopup({
433
        delegate: '.fancycover', // child items selector, by clicking on it popup will open
434
        type: 'image',
435
        gallery:{enabled:true, preload: [0,2]},
436
        disableOn: function() {
437
          if( getCurrentOption ("use_fancyapps") === "1" ) {
438
            return true;
439
          }
440
          return false;
441
        }
442
    });
443
}
444
445
window.onpopstate = function(event) {
446
    if (!isDefined (currentData)) {
447
        return;
448
    }
449
450
    before = new Date ();
451
    var data = cache.get (event.state);
452
    updatePage (data);
453
};
454
455
$(document).keydown(function(e){
456
    if (e.keyCode === 37 && $("#prevLink").length > 0) {
457
        navigateTo ($("#prevLink").attr('href'));
458
    }
459
    if (e.keyCode === 39  && $("#nextLink").length > 0) {
460
        navigateTo ($("#nextLink").attr('href'));
461
    }
462
});
463
464
/*exported initiateAjax */
465
function initiateAjax (url, theme) {
466
    $.when($.get('templates/' + theme + '/header.html'),
467
           $.get('templates/' + theme + '/footer.html'),
468
           $.get('templates/' + theme + '/bookdetail.html'),
469
           $.get('templates/' + theme + '/main.html'),
470
           $.get('templates/' + theme + '/page.html'),
471
           $.get('templates/' + theme + '/suggestion.html'),
472
           $.getJSON(url)).done(function(header, footer, bookdetail, main, page, suggestion, data){
473
        templateBookDetail = doT.template (bookdetail [0]);
474
475
        var defMain = {
476
            bookdetail: bookdetail [0]
477
        };
478
479
        templateMain = doT.template (main [0], undefined, defMain);
480
481
        var defPage = {
482
            header: header [0],
483
            footer: footer [0],
484
            main  : main [0],
485
            bookdetail: bookdetail [0]
486
        };
487
488
        templatePage = doT.template (page [0], undefined, defPage);
489
490
        templateSuggestion = doT.template (suggestion [0]);
491
492
        currentData = data [0];
493
494
        updatePage (data [0]);
495
        cache.put (url, data [0]);
496
        if (isPushStateEnabled) {
497
            history.replaceState(url, "", window.location);
498
        }
499
        handleLinks ();
500
    });
501
}