izi_lightup 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 130b95a322e27bff9b2b60080ca086f90d9a1a391d7cace27e479f78b9291585
4
+ data.tar.gz: b090a0d62203d7f4338c53eda32db9c64191686e0871d2bd59b14d075681d167
5
+ SHA512:
6
+ metadata.gz: 46424c06388df9af3110d69e781e4453713bb6d85dc3326e80fe8cab868152da360a0cd75c44088a12163040a7400677b96190f0f5879b8ce2797439bbd77f04
7
+ data.tar.gz: bc4da804bc238a6a158003362814c4aff7e2249b8c9ba2e0dfbcb967d5d6b064c63aa0cf8c8153873ff3d59aab955c5ea33d533e282d2dc57b1e389cd3969129
@@ -0,0 +1,3 @@
1
+ # Izi Lightup
2
+
3
+ Utils to speed up page load by using critical css & deferred scripts initialization
@@ -0,0 +1,73 @@
1
+ ((window, document) -> (
2
+ EVENT_TYPE = window.__activeEventName || 'Activity'
3
+ MAX_TIMEOUT = window.__activeTimeout || 5000
4
+ TRACK_MARKING = window.__activeMark || '_real_track=true'
5
+
6
+ if custom_mt = /^\?(\d+)$/.exec(window.location.search)
7
+ MAX_TIMEOUT = parseInt(custom_mt[1], 10)
8
+
9
+ window.activeReady = false
10
+ sent = false
11
+ window.activeOn = (callback) ->
12
+ if sent
13
+ return if typeof(callback) isnt 'function'
14
+ return requestAnimationFrame(callback) if window?.requestAnimationFrame?
15
+ callback()
16
+
17
+ fn = ->
18
+ document.removeEventListener EVENT_TYPE, fn
19
+ return if typeof(callback) isnt 'function'
20
+ return requestAnimationFrame(callback) if window?.requestAnimationFrame?
21
+ callback()
22
+
23
+ document.addEventListener(EVENT_TYPE, fn, {passive: true})
24
+
25
+ cleanListeners = undefined
26
+
27
+ sendActive = ->
28
+ return if sent
29
+ sent = true
30
+ try
31
+ document.dispatchEvent(new Event(EVENT_TYPE))
32
+ catch
33
+ oldEvt = document.createEvent('CustomEvent')
34
+ oldEvt.initCustomEvent(EVENT_TYPE, true, true, {})
35
+ document.dispatchEvent(oldEvt)
36
+
37
+ window.activeReady = true
38
+
39
+ activeForUser = ->
40
+ document.cookie = "#{TRACK_MARKING};path=/;max-age:31536000;samesite"
41
+ cleanListeners?()
42
+ sendActive()
43
+
44
+ untrack = ->
45
+ expTime = new Date()
46
+ expTime.setTime(0)
47
+ document.cookie = "#{TRACK_MARKING};expires=#{expTime.toGMTString()}"
48
+
49
+ if document.cookie.indexOf(TRACK_MARKING) isnt -1
50
+ setTimeout sendActive, 10
51
+
52
+ window.activeOn.untrack = untrack
53
+ return
54
+
55
+ document.addEventListener 'DOMContentLoaded', ->
56
+ body = document.body
57
+ lEvents = [
58
+ [body, 'mousemove'],
59
+ [body, 'scroll'],
60
+ [body, 'keydown'],
61
+ [body, 'click'],
62
+ [body, 'touchstart'],
63
+ [window, 'blur'],
64
+ [window, 'focus'],
65
+ ];
66
+ cleanListeners = ->
67
+ evt[0].removeEventListener(evt[1], activeForUser) for evt in lEvents
68
+
69
+ for evt in lEvents
70
+ evt[0].addEventListener(evt[1], activeForUser, {passive: true})
71
+
72
+ setTimeout(sendActive, MAX_TIMEOUT, {passive: true})
73
+ ))(window, document)
@@ -0,0 +1,3 @@
1
+ //= require ./active_emit
2
+ //= require ./mini_require
3
+ //= require ./wait_for
@@ -0,0 +1 @@
1
+ //= require ./measure/cls
@@ -0,0 +1,64 @@
1
+ ((window) -> (
2
+ # LCS COUNTER
3
+ LCS_OK = 0.1
4
+ LCS_BAD = 0.25
5
+
6
+ TTL_TIMEOUT = 2000
7
+ MAX_TIMEOUT = 15000
8
+
9
+ __print = (msg, value) ->
10
+ info_css = 'color: silver;'
11
+
12
+ if value >= LCS_BAD
13
+ result_css = 'color: red; font-weight: bold;'
14
+ tag_css = 'color: red; font-weight: bold;'
15
+ tag = 'ERROR'
16
+ else if value >= LCS_OK
17
+ result_css = 'color: orange; font-style: italic;'
18
+ tag_css = 'color: orange; font-weight: bold;'
19
+ tag = 'WARN'
20
+ else
21
+ result_css = 'color: green;'
22
+ tag_css = 'color: green; font-weight: bold;'
23
+ tag = 'OK'
24
+
25
+ console.debug '%cMEASURE: %c<%s> %c%s', info_css, tag_css, tag, result_css, msg
26
+
27
+ __ttlTimeout = undefined
28
+ __lcs = ->
29
+ clsValue = 0
30
+ __unsub = undefined
31
+ __finTimeout = undefined
32
+
33
+ performanceObserver = new PerformanceObserver (performanceEntryList) ->
34
+ performanceEntries = performanceEntryList.getEntries()
35
+
36
+ clearTimeout(__ttlTimeout) if __ttlTimeout?
37
+ __ttlTimeout = setTimeout __unsub, TTL_TIMEOUT
38
+ for entry in performanceEntries
39
+ if !entry.hadRecentInput
40
+ clsValue += entry.value
41
+ __print "lcs updated #{clsValue.toFixed(3)}(+#{entry.value.toFixed(3)})", clsValue
42
+
43
+ console.debug 'INIT LCS COUNTER...'
44
+ performanceObserver.observe
45
+ type: 'layout-shift'
46
+ buffered: true
47
+
48
+ __unsub = ->
49
+ performanceObserver.disconnect('layout-shift')
50
+ __ttlTimeout = undefined
51
+ if __finTimeout?
52
+ clearTimeout(__finTimeout)
53
+ __finTimeout = undefined
54
+ __print "result #{clsValue.toFixed(3)}", clsValue
55
+
56
+ __finTimeout = setTimeout __unsub, MAX_TIMEOUT
57
+
58
+ __check_offset = ->
59
+ if window.scrollY <= 300
60
+ # init LCS counter
61
+ setTimeout __lcs, 1
62
+
63
+ setTimeout __check_offset, 1
64
+ ))(window)
@@ -0,0 +1,56 @@
1
+ ((window, document) -> (
2
+ window.__required ||= {}
3
+
4
+ _onload = (id) ->
5
+ return if typeof(__required[id]) is 'undefined' || __required[id].loaded
6
+
7
+ __required[id].loaded = true
8
+ callbacks = __required[id].callbacks
9
+ return unless Array.isArray(callbacks)
10
+
11
+ while callbacks.length > 0
12
+ callback = callbacks.shift()
13
+ callback.call(window) if typeof(callback) is 'function'
14
+
15
+ _buildSubscriptions = (id, node) ->
16
+ data = __required[id]
17
+ return if data.loaded
18
+ data.callbacks ||= []
19
+
20
+ callback = node.onload
21
+ node.onload = -> _onload(id)
22
+ node.addEventListener('load', -> _onload(id)) if typeof(node.addEventListener) is 'function'
23
+ node.attachEvent('load', -> _onload(id)) if typeof(node.attachEvent) is 'function'
24
+
25
+ data.callbacks.push(callback) if typeof(callback) is 'function'
26
+
27
+ _subscribe = (id, callback) ->
28
+ return if typeof(__required[id]) is 'undefined'
29
+
30
+ data = __required[id]
31
+ return setTimeout(callback, 0) if data.loaded
32
+
33
+ data.callbacks.push(callback) if typeof(callback) is 'function'
34
+
35
+ window.miniRequire ||= (key, source_url, callback = undefined) ->
36
+ id = "source_#{key.replace(/[^a-z0-9_\-]+/ig, '_')}"
37
+
38
+ # subscribe only if already attached
39
+ return _subscribe(id, callback) if __required[id]?
40
+
41
+ __required[id] = {loaded: false, callbacks: []}
42
+ # subscribe only if no source
43
+ return _subscribe(id, callback) unless source_url?
44
+
45
+ # attach script
46
+ src = document.createElement('script')
47
+ src.id = id
48
+ src.async = true
49
+ src.defer = true
50
+ src.src = source_url
51
+ _buildSubscriptions(id, src)
52
+ _subscribe(id, callback)
53
+
54
+ document.body.appendChild(src)
55
+ true
56
+ ))(window, document)
@@ -0,0 +1,28 @@
1
+ ((window) -> (
2
+ window.waitFor ||= (key, deepCheck, interval) ->
3
+ (callback) ->
4
+ _timer = null
5
+ _called = false
6
+ setTimeout (->
7
+ unless _called
8
+ console.warn "not loaded component [#{key}] in #{waitFor.timeout}", typeof window[key]
9
+ ), waitFor.timeout
10
+ checker = ->
11
+ return if _called || typeof window[key] is 'undefined'
12
+
13
+ if typeof deepCheck is 'function'
14
+ return unless deepCheck.call(window[key])
15
+
16
+ clearInterval(_timer)
17
+ _called = true
18
+ return unless typeof callback is 'function'
19
+ callback.call(window[key], window[key])
20
+
21
+ _timer = setInterval(checker, interval || waitFor.tick)
22
+
23
+ checker()
24
+ null
25
+
26
+ window.waitFor.timeout ||= 30000
27
+ window.waitFor.tick ||= 100
28
+ ))(window)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CriticalHelper
4
+ def css_preload(name)
5
+ link = content_tag :link, '', rel: 'preload', href: stylesheet_path(name), as: 'style', onload: 'this.rel="stylesheet"'
6
+ noscript = content_tag :noscript do
7
+ content_tag :link, '', rel: 'stylesheet', href: stylesheet_path(name)
8
+ end
9
+
10
+ link + noscript
11
+ end
12
+
13
+ def inline_js(name)
14
+ IziLightup::InlineAsset.inline_js(name).html_safe
15
+ end
16
+
17
+ def inline_css(name)
18
+ raw = IziLightup::InlineAsset.inline_css(name).html_safe
19
+ raw = "<!-- CRIT CSS: #{name} -->".html_safe + raw if Rails.env.development?
20
+ raw
21
+ end
22
+
23
+ def critical_js
24
+ inline_js('crit-utils/bundle.js').presence || '<!-- CRIT JS NOT FOUND! -->'.html_safe
25
+ end
26
+
27
+ def critical_css
28
+ name = find_scoped_css('critical')
29
+ return '<!-- CRIT CSS NOT FOUND! -->'.html_safe if name.blank?
30
+
31
+ return inline_css(name) if Rails.env.production?
32
+
33
+ # inject measure js for development
34
+ inline_css(name) + inline_js('utils/measure.js')
35
+ end
36
+
37
+ def smart_picture(object, fields = %i[picture], versions = [], params = {})
38
+ return '' if object.blank?
39
+
40
+ IziLightup::SmartPicture.render(object, fields, versions, params)
41
+ end
42
+
43
+ private
44
+
45
+ def find_scoped_css(scope_name)
46
+ [
47
+ File.join(scope_name, "#{controller_path}_#{action_name}.css"),
48
+ File.join(scope_name, "#{controller_path}.css"),
49
+ File.join(scope_name, "#{controller_name}_#{action_name}.css"),
50
+ File.join(scope_name, "#{controller_name}.css"),
51
+ "#{scope_name}.css"
52
+ ].detect { |n| asset_exist?(n) }
53
+ end
54
+
55
+ def fetch_items(object, fields)
56
+ Array.wrap(fields).map do |name|
57
+ object.respond_to?(name) ? object.public_send(name) : nil
58
+ end.compact
59
+ end
60
+
61
+ def asset_exist?(name)
62
+ Rails.application.assets_manifest.find_sources(name)&.first&.present?
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'izi_lightup/version'
4
+ require 'pathname'
5
+
6
+ module IziLightup
7
+ class << self
8
+ def configure
9
+ yield self
10
+ end
11
+ end
12
+
13
+ autoload :InlineAsset, 'izi_lightup/inline_asset'
14
+ autoload :SmartPicture, 'izi_lightup/smart_picture'
15
+
16
+ class << self
17
+ def root_path
18
+ @root_path ||= Pathname.new(File.dirname(File.expand_path(__dir__)))
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'izi_lightup/engine'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IziLightup
4
+ class Engine < ::Rails::Engine
5
+ engine_name 'izi_lightup'
6
+
7
+ require_relative 'extentions/autoload_paths'
8
+
9
+ config.assets.precompile += %w[
10
+ crit-utils/measure.js
11
+ crit-utils/bundle.js
12
+
13
+ loadCSS.js
14
+ cssrelpreload.js
15
+ ]
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # isolate_namespace IziLightup
4
+ IziLightup::Engine.config.autoload_paths += %W[
5
+ #{IziLightup.root_path}/app/helpers
6
+ ]
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IziLightup
4
+ module InlineAsset
5
+ class << self
6
+ def inline_file(paths, format = nil)
7
+ Array.wrap(paths).map do |asset_path|
8
+ raw_source(with_ext(asset_path, format))
9
+ end.join("\n" * 3)
10
+ end
11
+
12
+ def inline_js(paths = [])
13
+ return '' if paths.blank?
14
+
15
+ "<script type='text/javascript'>#{inline_file(paths, :js)}</script>"
16
+ end
17
+
18
+ def inline_css(paths = [])
19
+ return '' if paths.blank?
20
+
21
+ "<style type='text/css'>#{inline_file(paths, :css)}</style>"
22
+ end
23
+
24
+ private
25
+
26
+ def with_ext(path, format = nil)
27
+ return path if format.nil?
28
+
29
+ ext = File.extname(path)
30
+ return path if ext.present?
31
+
32
+ "#{path}.#{format}"
33
+ end
34
+
35
+ def raw_source(asset_path)
36
+ manifest.find_sources(asset_path).first&.html_safe
37
+ end
38
+
39
+ def manifest
40
+ @manifest ||= Rails.application.assets_manifest
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IziLightup
4
+ module SmartPicture
5
+ include ActionView::Helpers::AssetTagHelper
6
+
7
+ class << self
8
+ def render(object, fields = %i[picture], versions = [], params = {})
9
+ return '' if object.blank?
10
+
11
+ items = fetch_items(object, fields)
12
+ return '' if items.blank?
13
+
14
+ versions = Array.wrap(versions)
15
+ items.each do |item|
16
+ next unless item.respond_to?(:data)
17
+
18
+ versions.each do |version_name|
19
+ next unless version_name == :default || item.data.respond_to?(version_name)
20
+
21
+ version = version_name == :default ? item.data : item.data.public_send(version_name)
22
+ next if version&.url&.blank?
23
+
24
+ url = version.url
25
+ params.merge!(%i[width height].zip(version.dimensions).to_h)
26
+ return image_tag(url, params) unless block_given?
27
+
28
+ yield(url, params)
29
+ return ''
30
+ end
31
+ end
32
+
33
+ ''
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IziLightup
4
+ VERSION = '0.0.0.1'
5
+ end
@@ -0,0 +1,43 @@
1
+ /*! CSS rel=preload polyfill. Depends on loadCSS function. [c]2016 @scottjehl, Filament Group, Inc. Licensed MIT */
2
+ (function( w ){
3
+ // rel=preload support test
4
+ if( !w.loadCSS ){
5
+ return;
6
+ }
7
+ var rp = loadCSS.relpreload = {};
8
+ rp.support = function(){
9
+ try {
10
+ return w.document.createElement( "link" ).relList.supports( "preload" );
11
+ } catch (e) {
12
+ return false;
13
+ }
14
+ };
15
+
16
+ // loop preload links and fetch using loadCSS
17
+ rp.poly = function(){
18
+ var links = w.document.getElementsByTagName( "link" );
19
+ for( var i = 0; i < links.length; i++ ){
20
+ var link = links[ i ];
21
+ if( link.rel === "preload" && link.getAttribute( "as" ) === "style" ){
22
+ w.loadCSS( link.href, link );
23
+ link.rel = null;
24
+ }
25
+ }
26
+ };
27
+
28
+ // if link[rel=preload] is not supported, we must fetch the CSS manually using loadCSS
29
+ if( !rp.support() ){
30
+ rp.poly();
31
+ var run = w.setInterval( rp.poly, 300 );
32
+ if( w.addEventListener ){
33
+ w.addEventListener( "load", function(){
34
+ w.clearInterval( run );
35
+ } );
36
+ }
37
+ if( w.attachEvent ){
38
+ w.attachEvent( "onload", function(){
39
+ w.clearInterval( run );
40
+ } )
41
+ }
42
+ }
43
+ }( this ));
@@ -0,0 +1,79 @@
1
+ /*! loadCSS: load a CSS file asynchronously. [c]2016 @scottjehl, Filament Group, Inc. Licensed MIT */
2
+ (function(w){
3
+ "use strict";
4
+ /* exported loadCSS */
5
+ var loadCSS = function( href, before, media ){
6
+ // Arguments explained:
7
+ // `href` [REQUIRED] is the URL for your CSS file.
8
+ // `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet <link> before
9
+ // By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document.
10
+ // `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
11
+ var doc = w.document;
12
+ var ss = doc.createElement( "link" );
13
+ var ref;
14
+ if( before ){
15
+ ref = before;
16
+ }
17
+ else {
18
+ var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
19
+ ref = refs[ refs.length - 1];
20
+ }
21
+
22
+ var sheets = doc.styleSheets;
23
+ ss.rel = "stylesheet";
24
+ ss.href = href;
25
+ // temporarily set media to something inapplicable to ensure it'll fetch without blocking render
26
+ ss.media = "only x";
27
+
28
+ // wait until body is defined before injecting link. This ensures a non-blocking load in IE11.
29
+ function ready( cb ){
30
+ if( doc.body ){
31
+ return cb();
32
+ }
33
+ setTimeout(function(){
34
+ ready( cb );
35
+ });
36
+ }
37
+ // Inject link
38
+ // Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs
39
+ // Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
40
+ ready( function(){
41
+ ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
42
+ });
43
+ // A method (exposed on return object for external use) that mimics onload by polling document.styleSheets until it includes the new sheet.
44
+ var onloadcssdefined = function( cb ){
45
+ var resolvedHref = ss.href;
46
+ var i = sheets.length;
47
+ while( i-- ){
48
+ if( sheets[ i ].href === resolvedHref ){
49
+ return cb();
50
+ }
51
+ }
52
+ setTimeout(function() {
53
+ onloadcssdefined( cb );
54
+ });
55
+ };
56
+
57
+ function loadCB(){
58
+ if( ss.addEventListener ){
59
+ ss.removeEventListener( "load", loadCB );
60
+ }
61
+ ss.media = media || "all";
62
+ }
63
+
64
+ // once loaded, set link's media back to `all` so that the stylesheet applies once it loads
65
+ if( ss.addEventListener ){
66
+ ss.addEventListener( "load", loadCB);
67
+ }
68
+ ss.onloadcssdefined = onloadcssdefined;
69
+ onloadcssdefined( loadCB );
70
+ return ss;
71
+ };
72
+ // commonjs
73
+ if( typeof exports !== "undefined" ){
74
+ exports.loadCSS = loadCSS;
75
+ }
76
+ else {
77
+ w.loadCSS = loadCSS;
78
+ }
79
+ }( typeof global !== "undefined" ? global : this ));
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: izi_lightup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - IzikAJ
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ Utils to speed up page load by using critical css &
15
+ deferred scripts initialization
16
+ email: izikaj@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.rdoc
22
+ - app/assets/javascripts/crit-utils/active_emit.js.coffee
23
+ - app/assets/javascripts/crit-utils/bundle.js
24
+ - app/assets/javascripts/crit-utils/measure.js
25
+ - app/assets/javascripts/crit-utils/measure/cls.js.coffee
26
+ - app/assets/javascripts/crit-utils/mini_require.js.coffee
27
+ - app/assets/javascripts/crit-utils/wait_for.js.coffee
28
+ - app/helpers/critical_helper.rb
29
+ - lib/izi_lightup.rb
30
+ - lib/izi_lightup/engine.rb
31
+ - lib/izi_lightup/extentions/autoload_paths.rb
32
+ - lib/izi_lightup/inline_asset.rb
33
+ - lib/izi_lightup/smart_picture.rb
34
+ - lib/izi_lightup/version.rb
35
+ - vendor/assets/javascripts/cssrelpreload.js
36
+ - vendor/assets/javascripts/loadCSS.js
37
+ homepage: https://bitbucket.org/netfixllc/izi_lightup
38
+ licenses: []
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.0.3
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Izi Lightup
59
+ test_files: []