diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..292c152 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +smart-app-banner.js diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..26ff6ea --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,19 @@ +env: + browser: true + node: true +extends: 'eslint:recommended' +rules: + no-undef: + - ignore + indent: + - error + - tab + linebreak-style: + - error + - unix + quotes: + - error + - single + semi: + - error + - always diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..d485fb2 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,19 @@ +{ + "rules": { + "block-no-empty": null, + "color-no-invalid-hex": true, + "comment-empty-line-before": [ "always", { + "ignore": ["stylelint-commands", "between-comments"], + } ], + "declaration-colon-space-after": "always", + "indentation": ["space", { + "except": ["value"] + }], + "max-empty-lines": 2, + "rule-nested-empty-line-before": [ "always", { + "except": ["first-nested"], + "ignore": ["after-comment"], + } ], + "unit-whitelist": ["em", "rem", "%", "s"] + } +} diff --git a/index.js b/index.js index ce9b220..1fe43ee 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,12 @@ var extend = require('xtend/mutable'); var q = require('component-query'); var doc = require('get-doc'); -var root = doc && doc.documentElement; var cookie = require('cookie-cutter'); var ua = require('ua-parser-js'); + +var root = doc && doc.documentElement; + +/* global navigator */ var userLang = navigator.language.slice(-2) || navigator.userLanguage.slice(-2) || 'us'; // platform dependent functionality @@ -11,27 +14,27 @@ var mixins = { ios: { appMeta: 'apple-itunes-app', iconRels: ['apple-touch-icon-precomposed', 'apple-touch-icon'], - getStoreLink: function() { + getStoreLink: function () { return 'https://itunes.apple.com/' + this.options.appStoreLanguage + '/app/id' + this.appId; } }, android: { appMeta: 'google-play-app', iconRels: ['android-touch-icon', 'apple-touch-icon-precomposed', 'apple-touch-icon'], - getStoreLink: function() { + getStoreLink: function () { return 'http://play.google.com/store/apps/details?id=' + this.appId; } }, windows: { appMeta: 'msApplication-ID', iconRels: ['windows-touch-icon', 'apple-touch-icon-precomposed', 'apple-touch-icon'], - getStoreLink: function() { + getStoreLink: function () { return 'http://www.windowsphone.com/s?appid=' + this.appId; } } }; -var SmartBanner = function(options) { +var SmartBanner = function (options) { var agent = ua(navigator.userAgent); this.options = extend({}, { daysHidden: 15, @@ -55,18 +58,15 @@ var SmartBanner = function(options) { this.type = this.options.force; } else if (agent.os.name === 'Windows Phone' || agent.os.name === 'Windows Mobile') { this.type = 'windows'; - //iOS >= 6 has native support for SmartAppBanner - } else if (agent.os.name === 'iOS' && parseInt(agent.os.version) < 6) { + // iOS >= 6 has native support for SmartAppBanner + } else if (agent.os.name === 'iOS' && Number(agent.os.version) < 6) { this.type = 'ios'; } else if (agent.os.name === 'Android') { this.type = 'android'; } // Don't show banner if device isn't iOS or Android, website is loaded in app, user dismissed banner, or we have no app id in meta - if (!this.type - || navigator.standalone - || cookie.get('smartbanner-closed') - || cookie.get('smartbanner-installed')) { + if (!this.type || navigator.standalone || cookie.get('smartbanner-closed') || cookie.get('smartbanner-installed')) { return; } @@ -83,12 +83,12 @@ var SmartBanner = function(options) { SmartBanner.prototype = { constructor: SmartBanner, - create: function() { + create: function () { var link = this.getStoreLink(); var inStore = this.options.price[this.type] + ' - ' + this.options.store[this.type]; var icon; for (var i = 0; i < this.iconRels.length; i++) { - var rel = q('link[rel="'+this.iconRels[i]+'"]'); + var rel = q('link[rel="' + this.iconRels[i] + '"]'); if (rel) { icon = rel.getAttribute('href'); break; @@ -100,52 +100,50 @@ SmartBanner.prototype = { sb.innerHTML = '
'; - //there isn’t neccessary a body + // there isn’t neccessary a body if (doc.body) { doc.body.appendChild(sb); - } - else if (doc) { - doc.addEventListener('DOMContentLoaded', function(){ + } else if (doc) { + doc.addEventListener('DOMContentLoaded', function () { doc.body.appendChild(sb); }); } q('.smartbanner-button', sb).addEventListener('click', this.install.bind(this), false); q('.smartbanner-close', sb).addEventListener('click', this.close.bind(this), false); - }, - hide: function() { + hide: function () { root.classList.remove('smartbanner-show'); }, - show: function() { + show: function () { root.classList.add('smartbanner-show'); }, - close: function() { + close: function () { this.hide(); cookie.set('smartbanner-closed', 'true', { path: '/', - expires: +new Date() + this.options.daysHidden * 1000 * 60 * 60 * 24 + expires: Number(new Date()) + (this.options.daysHidden * 1000 * 60 * 60 * 24) }); }, - install: function() { + install: function () { this.hide(); cookie.set('smartbanner-installed', 'true', { path: '/', - expires: +new Date() + this.options.daysReminder * 1000 * 60 * 60 * 24 + expires: Number(new Date()) + (this.options.daysReminder * 1000 * 60 * 60 * 24) }); }, - parseAppId: function() { + parseAppId: function () { var meta = q('meta[name="' + this.appMeta + '"]'); if (!meta) { return; diff --git a/package.json b/package.json index 2311f7e..a959ff7 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "Smart banner for Android or iOS", "main": "index.js", "scripts": { - "build": "browserify index.js -s SmartBanner | ccjs - > smart-app-banner.js" + "build": "browserify index.js -s SmartBanner | ccjs - > smart-app-banner.js", + "precommit": "xo --fix index.js && fixmyjs && stylefmt smart-app-banner.css && npm run test && npm run build", + "test": "xo" }, "repository": { "type": "git", @@ -45,6 +47,11 @@ "devDependencies": { "browserify": "^10.2.4", "ccjs": "^0.2.0", - "closurecompiler": "^1.5.1" + "closurecompiler": "^1.5.1", + "eslint": "^3.9.1", + "fixmyjs": "^1.0.3", + "jshint": "^2.9.4", + "stylefmt": "^4.3.1", + "xo": "^0.17.0" } } diff --git a/smart-app-banner.css b/smart-app-banner.css index df6757c..60e8108 100644 --- a/smart-app-banner.css +++ b/smart-app-banner.css @@ -1,6 +1,7 @@ .smartbanner-show { margin-top: 80px; } + .smartbanner-show .smartbanner { display: block; } @@ -39,7 +40,7 @@ text-decoration: none; border: 0; border-radius: 14px; - -webkit-font-smoothing: subpixel-antialiased; + -webkit-font-smoothing: subpixel-antialiased; } .smartbanner-close:active, @@ -63,11 +64,11 @@ width: 44%; font-size: 11px; line-height: 1.2em; - font-weight: bold; + font-weight: bold; } .smartbanner-title { - font-size:13px; + font-size: 13px; line-height: 18px; } @@ -88,7 +89,8 @@ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); } -.smartbanner-button:active, .smartbanner-button:hover { +.smartbanner-button:active, +.smartbanner-button:hover { color: #aaa; } @@ -112,10 +114,10 @@ width: 18px; height: 18px; line-height: 18px; - font-family: Arial; + font-family: Arial; color: #888; text-shadow: 0 1px 0 white; - -webkit-font-smoothing: none; + -webkit-font-smoothing: none; } .smartbanner-ios .smartbanner-close:active, @@ -127,23 +129,23 @@ background-size: cover; } -.smartbanner-ios .smartbanner-info { +.smartbanner-ios .smartbanner-info { color: #6a6a6a; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); - font-weight:300; + font-weight: 300; } .smartbanner-ios .smartbanner-title { - color:#4d4d4d; + color: #4d4d4d; font-weight: 500; } .smartbanner-ios .smartbanner-button { - padding: 0 10px; - font-size: 15px; - min-width: 10%; - font-weight: 400; - color: #0C71FD; + padding: 0 10px; + font-size: 15px; + min-width: 10%; + font-weight: 400; + color: #0c71fd; } .smartbanner-ios .smartbanner-button:active, @@ -162,7 +164,7 @@ /** Android **/ .smartbanner-android { background: #3d3d3d url('dark_background_stripes.gif'); - box-shadow: inset 0 4px 0 #88B131; + box-shadow: inset 0 4px 0 #88b131; line-height: 82px; } @@ -189,23 +191,23 @@ } .smartbanner-android .smartbanner-info { - color:#ccc; - text-shadow:0 1px 2px #000; + color: #ccc; + text-shadow: 0 1px 2px #000; } .smartbanner-android .smartbanner-title { - color:#fff; + color: #fff; font-weight: bold; } .smartbanner-android .smartbanner-button { min-width: 12%; color: #d1d1d1; - font-weight: bold; + font-weight: bold; padding: 0; background: none; border-radius: 0; - box-shadow: 0 0 0 1px #333, 0 0 0 2px #DDDCDC; + box-shadow: 0 0 0 1px #333, 0 0 0 2px #dddcdc; } .smartbanner-android .smartbanner-button:active, @@ -217,8 +219,8 @@ text-align: center; display: block; padding: 0 10px; - background: #42B6C9; - background: linear-gradient(to bottom, #42B6C9, #39A9BB); + background: #42b6c9; + background: linear-gradient(to bottom, #42b6c9, #39a9bb); text-transform: none; text-shadow: none; box-shadow: none; @@ -226,7 +228,7 @@ .smartbanner-android .smartbanner-button-text:active, .smartbanner-android .smartbanner-button-text:hover { - background: #2AC7E1; + background: #2ac7e1; } @@ -253,9 +255,9 @@ } .smartbanner-windows .smartbanner-icon { - background: rgba(0,0,0,0.6); + background: rgba(0, 0, 0, 0.6); background-size: cover; - box-shadow: 0 1px 3px rgba(0,0,0,0.3); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } .smartbanner-windows .smartbanner-info { @@ -264,7 +266,7 @@ } .smartbanner-windows .smartbanner-title { - color:#4d4d4d; + color: #4d4d4d; font-weight: bold; } diff --git a/smart-app-banner.js b/smart-app-banner.js index de403c9..8545590 100644 --- a/smart-app-banner.js +++ b/smart-app-banner.js @@ -1,33 +1,34 @@ -(function(t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"===typeof define&&define.amd?define([],t):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).SmartBanner=t()})(function(){return function d(g,f,e){function b(c,n){if(!f[c]){if(!g[c]){var k="function"==typeof require&&require;if(!n&&k)return k(c,!0);if(a)return a(c,!0);k=Error("Cannot find module '"+c+"'");throw k.code="MODULE_NOT_FOUND",k;}k=f[c]= -{exports:{}};g[c][0].call(k.exports,function(a){var e=g[c][1][a];return b(e?e:a)},k,k.exports,d,g,f,e)}return f[c].exports}for(var a="function"==typeof require&&require,c=0;c