PageSourceSearch

https://codelamp.co.uk/js/site.js?build=BUILDTOKEN

js codelamp.co.uk collected 2026-10-04 13:34:42 UTC 12,314 bytes, 395 lines download raw bytes

1/**
2 * @requires makeshift
3 * @requires gtrack
4 * @requires jQuery
5 * @requires introJs
6 * @requires solarDisplay
7 * @requires steps
8 */
9var makeshift, gtrack, introJs, solarDisplay, steps;
10/**
11 * ##### Overview
12 *
13 * As for nearly all my sites, I have a global `site` namespace.
14 *
15 * I find it useful having a singular global object that methods can be attached
16 * to that would be categorised as general code for the site. Obviously, if
17 * the code can be placed under any other namespace (a more specific one) then
18 * it makes sense to do that instead.
19 *
20 * ##### CHANGE LOG
21 *
22 * -- 20 April 2017
23 * - Refactored numerous scripts to finish off the site
24 *
25 * -- 9 March 2016
26 *
27 * - Fixed Firefox by forcing all image layers to render to canvas.
28 * - Added hammer touch handling (WIP).
29 *
30 * -- 6 March 2016
31 *
32 * - Added support to switch cleanly between JS Transitions and CSS Transitions.
33 * - Split makeshift down into smaller modules.
34 *
35 * @namespace site
36 * @author Phil Glanville
37 */
38var siteCreator = makeshift.generateSite(jQuery, window).extend({
39
40  /**
41   * Check if the site is in introducing mode
42   * @memberof! site
43   */
44  isIntroducing: function(){
45    return this.body.is('.introducing');
46  },
47
48
49
50  /**
51   * To help with exporting the rendered version of the site
52   * @memberof! site
53   */
54  exportSource: function(){
55    if ( window.sourceFrame ) {
56      window.sourceFrame.remove();
57      window.sourceFrame = null;
58      return;
59    }
60    jQuery('body').removeClass('with-js');
61    window.sourceFrame = jQuery('<iframe />').hide()
62      .appendTo('body')
63      .css({
64        position: 'absolute',
65        left: 0,
66        top: 0,
67        right: 0,
68        bottom: 0,
69        width: '100%',
70        height: '100%',
71        zIndex: 10000
72      })
73      .attr('src',
74        'data:text/html,' +
75        encodeURIComponent(
76          '<!DOCTYPE html><html><body>' +
77          '<style>body { color: white; background: rgba(0,0,0,0.8); font-size: 8px; }</style>' +
78          '<script>document.write("<xmp><!DOCTYPE html><html>" + window.parent.document.documentElement.innerHTML + "</html></xmp>");window.parent.sourceFrame.show();</script>' +
79          '</body></html>'
80        )
81      )
82    ;
83  },
84
85  /**
86   * A quick hack method to allow iframe page content to be "lazy-loaded" but
87   * still also be workable for non-js browsers.
88   *
89   * ##### For non-JS browsers
90   *
91   * Without js, all the page iframes are targeted to a page that has a link,
92   * this link states "click to load content". When the iframe is called into
93   * existence, the user has the choice to click the link, causing the "lazy load".
94   *
95   * ##### For JS-enabled browsers
96   *
97   * `site.autoclick` is only triggered by js-browsers, and is executed by clicking the link
98   * that calls the iframe into vision. The function then "autoclicks" the link in the iframe,
99   * mentioned in the non-js version above. This triggers the load of the actual iframe
100   * content in a "on-request" manner.
101   *
102   * Because the iframe is triggered its load at the point it is requested, to avoid FOUC,
103   * the code hides the iframe - until the iframe content triggers `site.autoclickComplete`.
104   *
105   * @see site.autoclickComplete
106   * @memberof! site
107   * @param {HTMLElement} iframe - iframe element that is to be auto-navigated
108   * @param {String} target - html selector for the link to click inside the iframe
109   */
110  autoclick: function(iframe, target){
111    if ( !this.autoclick.list ) {
112      this.autoclick.keys = [];
113      this.autoclick.list = [];
114    }
115    iframe = jQuery(iframe);
116    target = iframe.contents().find(target);
117    if ( !target.length ) { return; }
118    var self = this;
119    var iwin = iframe.get(0).contentWindow;
120    this.autoclick.keys.push(iwin);
121    this.autoclick.list.push({
122      tid: setTimeout(function(){ self.autoclickComplete(iwin); }, 2000),
123      target: target,
124      iframe: iframe.addClass('hidden-iframe'),
125      started: new Date().getTime(),
126      minimum: 2000
127    });
128    if ( target.attr('data-href') ) {
129      target.length && iframe.attr('src', target.attr('data-href'));
130    }
131    else {
132      target.length && iframe.attr('src', target.attr('href'));
133    }
134  },
135
136  /**
137   * Should be called by the loading iframe, to finalise the 'autoclick'
138   *
139   * @see site.autoclick
140   * @memberof! site
141   */
142  autoclickComplete: function(iwin){
143    if ( !this.autoclick.keys ) return;
144    var self = this, d, i = this.autoclick.keys.indexOf(iwin), item;
145    if ( i != -1 ) {
146      item = this.autoclick.list[i];
147      item.tid && clearTimeout(item.tid);
148      d = (new Date().getTime() - item.started);
149      if ( d < item.minimum ) {
150        item.tid = setTimeout(function(){
151          self.autoclickComplete(iwin);
152        }, (item.minimum - d) + 10);
153      }
154      else {
155        item.iframe.removeClass('hidden-iframe');
156        site.readyGratuitousTracking(iwin.document);
157      }
158    }
159  },
160
161  /**
162   * Trigger the start of the audioscape system, it is a layer within the parallax
163   * that responds to an audiotrack. The equaliser levels are calculated at runtime
164   * using the AudioContext API.
165   *
166   * @memberof! site
167   */
168  audioscape: function(){
169    if ( !this.audioscape.playing ) {
170      this.audioscape.playing = true;
171      jQuery(window).triggerHandler('audiolandscapestart');
172    }
173    else {
174      this.audioscape.playing = false;
175      jQuery(window).triggerHandler('audiolandscapestop');
176    }
177  },
178
179  /**
180   * Enable/disable touch controls
181   *
182   * @memberof! site
183   */
184  touchmode: function(icon){
185    icon = jQuery(icon);
186    if ( icon.is('.enabled') ) {
187      icon.removeClass('enabled');
188      this.parallax.touchDrag.disable();
189    }
190    else {
191      icon.addClass('enabled');
192      this.parallax.touchDrag.enable();
193    }
194  },
195
196  /**
197   * Trigger the site controls panel
198   *
199   * @memberof! site
200   */
201  controls: function(){
202    jQuery('body').toggleClass('controls-popover');
203  },
204
205  /**
206   * Trigger the StackOverflow panel
207   *
208   * @memberof! site
209   */
210  showSO: function(){
211    var i = introJs().setOptions({
212      "showProgress": false,
213      "showBullets": false,
214      "showButtons": false,
215      "showStepNumbers": false,
216      "scrollToElement": false,
217      "steps": [{
218        "element": ".item-stackoverflow",
219        "intro":
220          "<span class=\"text-content\"><h4>Stack Overflow</h4><p>I frequently help answer questions on Stack Overflow." +
221          "It is a great way to research and learn in a random way. You'd be surpised at the number of odd requests " +
222          "people make, that end up changing your own approach.</p><br>" +
223          "<a href=\"http://stackoverflow.com/users/1490904/pebbl\">" +
224            "<img width=\"208\" height=\"58\" title=\"profile for Pebbl at Stack Overflow, Q&amp;A for professional and enthusiast programmers\" alt=\"profile for Pebbl at Stack Overflow, Q&amp;A for professional and enthusiast programmers\" src=\"http://stackoverflow.com/users/flair/1490904.png?theme=dark\">" +
225          "</a>" +
226          "</span>",
227        "position": "bottom"
228      }]
229    });
230    i.start();
231  },
232
233  /**
234   * Assign the loaded content to the parallax system - loaded from JSON/HTML snippets.
235   *
236   * @memberof! site
237   */
238  readyParallax: function(){
239    //this.tick(this.parallax.tick);
240    this.content && this.parallax.assignContent(this.content);
241  },
242
243  /**
244   * Simple function to set-up the top right menu, enabling click events
245   *
246   * @memberof! site
247   */
248  readyMenu: function(){
249    var site = this;
250    // @TODO: quick fix, needs work
251    var hideSuperflous = function(){
252      jQuery('.item-description').hide();
253      jQuery('#mediaq').hide();
254      site.off('pagemarkchange', hideSuperflous);
255    };
256    jQuery('.item-introduction a')  .click(function(){ site.intro(); });
257    jQuery('.item-music a')         .click(function(){ site.audioscape(); });
258    jQuery('.item-fullscreen a')    .click(function(){ site.fullscreen(); });
259    jQuery('.item-power a')         .click(function(){ site.power(this); });
260    jQuery('.item-controls a')      .click(function(){ site.controls(); });
261    jQuery('.item-touchmode a')     .click(function(){ site.touchmode(this); });
262    jQuery('.item-stackoverflow a') .click(function(){ site.showSO(); });
263    if ( this.parallax.justTouch ) {
264      jQuery('.item-touchmode a').click();
265    }
266    this.on('pagemarkchange', hideSuperflous);
267  },
268
269  /**
270   * ready the keyboard controlls for the site using Keyboard.js
271   *
272   * @memberof! site
273   */
274  readyControls: function(){
275    var site = this;
276    this.registerSimpleCombo('x',     function(e, count){ if ( site.isIntroducing() ) { return; } site.exportSource(); });
277    this.registerSimpleCombo('q',     function(e, count){ if ( site.isIntroducing() ) { return; } site.parallax.pagemark.y('+1').update(); });
278    this.registerSimpleCombo('down',  function(e, count){ if ( site.isIntroducing() ) { return; } site.parallax.pagemark.z('+1').update(); });
279    this.registerSimpleCombo('up',    function(e, count){ if ( site.isIntroducing() ) { return; } site.parallax.pagemark.z('-1').update(); });
280    this.registerSimpleCombo('left',  function(e, count){ if ( site.isIntroducing() ) { return; } site.parallax.pagemark.x('-1').update(); });
281    this.registerSimpleCombo('right', function(e, count){ if ( site.isIntroducing() ) { return; } site.parallax.pagemark.x('+1').update(); });
282    this.registerSimpleCombo('w',     function(e, count){ if ( site.isIntroducing() ) { return; } site.parallax.pagemarks.toggleState('offscreen', 'initial'); });
283    this.registerSimpleCombo('g',     function(e, count){ jQuery('body').toggleClass('crazy-mode'); });
284  },
285
286  readyGratuitousTracking: function(context){
287    console.log('tracking context', context||document);
288    try { gtrack(context||document); }catch( ex ) { console.log('tracking failed', ex); };
289  },
290
291  /**
292   * Because it is possible to load directly on an iframe-d page, at
293   * least with js we can fix this. Those without js will need to click the load link.
294   */
295  readyFrameContent: function(){
296    var site = this;
297    var runonce = function(){
298      site.off('pagemarkchange', runonce);
299      var pm = site.parallax.pagemark.get(), target = jQuery('.more-info[href*="#'+pm+'"]');
300      if ( target.length ) {
301        target.click();
302        console.log('frame auto clicked for', pm);
303      }
304    };
305    site.on('pagemarkchange', runonce);
306  }
307
308});
309
310/**
311 * Build an instance of site
312 */
313var site = siteCreator.create({
314
315  /**
316   * The JSON source that powers the current site
317   *
318   * @memberof! site
319   * @type string
320   */
321  source: String(window.location).indexOf('making') != -1 ? 'data/making.json?' : 'data/core.json?',
322
323  /**
324   * The frameRate of the tick operations, currently only used by the
325   * site when using js-transtions. These have been disabled in v4 of the
326   * site... until I get more time to refactor.
327   *
328   * @memberof! site
329   * @type number
330   */
331  frameRate: 40,
332
333  /**
334   * hud configuration
335   *
336   * @memberof! site
337   */
338  hud: {
339    target: 'nav.hud'
340  },
341
342  /**
343   * progress configuration
344   *
345   * @memberof! site
346   */
347  progress: {
348    target: '#loader',
349    canvas: {
350      width: 100,
351      height: 100
352    },
353    hints: {
354      parallax: 'rgba(255, 128, 0, 1)',
355      content: 'rgba(255, 200, 0, 1)'
356    }
357  },
358
359  /**
360   * Triggered once preload is completed.
361   *
362   * @memberof! site
363   * @returns {undefined}
364   */
365  ready: function(){
366    this.readyParallax();
367    this.readyFrameContent();
368    this.readyControls();
369    this.readyMenu();
370    this.readyGratuitousTracking();
371
372    // specify CSS transitions to start
373    this.parallax && this.parallax.useCSSTransitions();
374
375    // some links have previews
376    jQuery('.use-preview').thumbPreview();
377
378    // handle the reveal of the website
379    steps.create().context(this)
380      .addDelay(250)
381      .addWithDelay(250, !this.parallax.pagemark.is('pm000') && function(){ this.parallax.pagemark.force(); })
382      .addWithDefer(function(d){
383        this.ui.progress.fadeOut(function(){
384          d.resolve();
385        });
386      })
387      .addWithDelay(500, function(){
388        this.parallax.pagemarks.changeState('initial');
389      })
390      .start()
391    ;
392    //site.parallax.shiftTo(-8000, 0, 500, { duration: 10000 })
393  }
394
395});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.