izi_lightup 0.0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []