working and tested

This commit is contained in:
David Tai
2015-07-22 17:30:11 -07:00
parent f4532af10b
commit d6e8581cc4
15 changed files with 1081 additions and 329 deletions
+3 -2
View File
@@ -1,9 +1,10 @@
before-install:
- export TRAVIS_COMMIT_MSG="$(git log --format=%B --no-merges -n 1)"
env:
env:
global:
- secure: jVZVvtvRSEHDoG6W3Ptq0kw19ZnJTYIQrKZ0SxhV2CQMwgKY5nDsQXUqbfPpQktPOKyLeMb2DljwkGRC6UZ+j6UEy+O3+MS5YBrE6svkcaBgx8A4ck+S4rddKAzf6rpB4h+EfqdVXNknY0wulUy1vc2oCM9o/P1MMu8jOqeSQglLemFZdQS4DHLy1zecjY2ovX18sql+u995r8tDmoNEt00zrZo+UmWN9TP0eag2/+ZHSLWFpWlZF2E1HctZ+iFN9KgcZ277QbGKcIOXYcc5IbyHdwuG+EHU4sH9/d3d687g8GbeTpQBynO/SwQKs/6uATQrYgtvq47fbPKYjk025awXmXvVskwZg9obKsOT3F+NA4xiyzgnQ/RnW+FOP4CUKUn7eXG64c86PFG+cUdLQNmM7MKOyb3dzdN2bUQX1kUx3ZBCV2gWNf5Hhod2ntvC7zY7naN4fV9wRjQJ19ZXSUKpZJOl4iPQQTurFs75Mct09R4Z6UXFlcVyBe2Wj3J1G9SiJOMM0vGvyDbdt9rbkD9ahIbHHK7X8c4xpwZczqq5hJoNKnltdKZf/BOOmfUW9PB4Nkg9kbc7/Had/6Q3xHYzo5LjRXNvVIan2dgIlUkSzGfIskS+I0cYrR1rwa8w7e7dzyDf1WAcHOi6aVYPDkMwjqOSDTXua/FyJXVCGHk=
- secure: I4Dsa7rN1p1RMCt7K2ElPofcfgJoEieiWihxI4MNkmcAD33Wz2O1HEp0dfzBS6lTPulb6MfC6XvWLAtNgfroYV/C28Wu1sNn4M7GzqdD0MZ7S9mfHWl+vUlp+1ptV8bUPOez3vk2R41wEG6GIp1EIr/xuhfgsMikg5IkQST/OOMKagMALM0aQVrewaiORNjbxoCpSf4zZ8dyE5kLAe1ZICRv6DgZ+vJjCosikp/lYauqlxiQdzio3RkBa049xu7rCBipbvkl+k5CLxdwHtfw60UaGj9/FKjqrZza+45TevaUwxehhSD6GkoPkbw/h+7VPj+exZ7RDmT7JrDinTm2sDNsz2FDA0DSs0HdOV6+K48tAUTEpGR+BUedtgXomE6Tt0Evr4qNxSpgns+vQ4dlSLoW/AHNxVUQ5EtIcpPnPs26hlbn66rFLZriyqMndImHh5XqhoXvsHEI1467ozd2TqS3n13GORz6EwgzCrY4OGBJiI35RL2RJv+OXcTsTY3hI4wFP5sIdFuilTTwNPIuUKLLTkNbjAlPA+/8lmQXwrOED2bNWXo+iBfRd+PiYHm3KXCZRFJSeoJbDd0kmxd3i6RQEh1oZKp7keJ7UIAIgwZ+girNI6mSkGiHteqToJnLsk4sFf5reA58tdfFa9bQbUsXrezfwnnFhOnEkVCuqwU=
- secure: OVCBOhg2IilVc3qjj+DdCrewXxH4LqB7pyQAtVOP2nVegV2ZYVD9AZjjSp2gcd3GgTDWyFNbjta9qwn1i3aVFt+TfH2U5NMjleZTlWPgBCN9XJPQI3SNiMzYDI+qcEWcbNSRkSKFx5xFI9s3QaB9wWOMy0/uNSMo8OokMWMUZjICGMXyN0xYGbUskGDff4BBge13Qjboc2c24wTU5WvlGT8mSy0g10AofbYDI5LkDnVmbqSv0KcxzVlUovFnqtOFphzOZiLLm4X64WJKcHVsQbImNpfSGNFnTpBDKmom0CVs19sl+bznPtR/6/n0vQDmm6Wfu9d5JuOYGOMapUpWmwRXMRA9MSSDjhSGtrm01G+/crRdFO+00vSBAxZjocpWldX5hrQGNbjhw+CU+BKw57T3vZ+5lpb4DlAmU9TbplZ10k54D06Z8lOtQN11ZAXkrhccVIdGfYzDwcg21xvkR+SVoq8+np/9ex4ps5gBpsDWAQ5pHBDp6sEX9JSzRZQFR3xuSB/zkwcTe2mozaqzr9rRc5A83M/OOKmHoPyuNI8TnzMQQP2Hl35W15ab5anbhpUmpEhlea8gQsC8ktEgYPfL+x75XwhY/ooPUtSqIH7wZYJms7niXeLicamws4GpM65w02G6z5kMEAH1wJkEY458PvVlWsIeKDO1Hn1YC7k=
- secure: Nf6BEvDEr5O4l0J+G00TfPKuTCn15d+d9YKR8DkqMkKpnZkZO0M+PsbeXe6fZcxuXcTFvOYxFSv/YaEVNe+IUiHHpExdrk7lxpuKN4gI0oSWXojtKKkt1GZ85vZDL3vnAHYKT07uEZNHsONziNuR3Dy7GeGjN/irb9OgmXoiXmVciKZiuNP1q5Msr409V6BWMvFx2qL5M7eDzjHW/Oh7LiFsNFPcrUhzHe8SqUhOTlfkfz3YpZvh80NCwnfiZNP0yVBRIpHZDFnzgTZimNwuS3ya479FBR6Oyt8gKOe7VoiLfef+/kyAMy8moXO9722bi4nay4ic7MJ0uRBeETgEtgdby/nk/5toJvO3nmX0JSGc24rJqLZbAjoXjJlg2gAaCscsNaQGFh5VJ4Ih4CTTc1s9/D4LpgkcX4/rahq5WK89XHY6ps1LCqskC/5NUoNy1844gFmk3SzZq6/oOpVQAA/R50wGj03SqGZqaoEtjf2xhBtiSsw7zRhMMs/FR/HWhGjVgquUX+y74B437T0csddBX/BIsA1ThjV3ENiJPSJwQnbDuZUBE7WhnRv7S6lIP0F/duW3DqxMpuojZdSCiazIaTSljggh7aavib1EcZy4z1Gpslcgov9eKlVnX4/BVos2wwbRvj5Zt3+ku8fSienoqu/8a5QudbhJm5SbySM=
addons:
sauce_connect: true
language: node_js
+1 -2
View File
@@ -13,8 +13,7 @@ task 'build', 'build project', (options) ->
exec 'node_modules/.bin/coffee -bcm -o .test/ test/'
task 'watch', 'watch for changes and recompile project', ->
exec 'node_modules/.bin/coffee -bcmw -o lib/ src/'
exec 'node_modules/.bin/coffee -bcmw -o .test test/'
exec 'node_modules/.bin/bebop'
task 'publish', 'publish project', (options) ->
newVersion = options.version ? 'patch'
+30
View File
@@ -0,0 +1,30 @@
fs = require 'fs'
path = require 'path'
exec = require('executive').interactive
requisite = 'node_modules/.bin/requisite -g'
compile = ->
exec 'node_modules/.bin/coffee -bcm -o lib/ src/'
exec 'node_modules/.bin/requisite src/index.coffee -o inform.js'
exec 'node_modules/.bin/requisite src/index.coffee -m -o inform.min.js'
module.exports =
port: 4242
cwd: process.cwd()
exclude: [
/css/
/lib/
/node_modules/
/vendor/
]
compilers:
coffee: (src) ->
if /^src/.test src
compile()
if /src\/index.coffee/.test src
compile()
+206
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
(function(e){function t(e,n){if({}.hasOwnProperty.call(t.cache,e))return t.cache[e];if("function"==typeof n)return void t.load(e,n);var r=t.resolve(e);if(!r)throw new Error("Failed to resolve module "+e);var i={id:e,require:t,filename:e,exports:{},loaded:!1,parent:null,children:[]},o=e.slice(0,e.lastIndexOf("/")+1);return t.cache[e]=i.exports,r.call(i.exports,i,i.exports,o,e),i.loaded=!0,t.cache[e]=i.exports}t.modules={},t.cache={},t.resolve=function(e){return{}.hasOwnProperty.call(t.modules,e)?t.modules[e]:void 0},t.define=function(e,n){t.modules[e]=n},t.define("little-emitter/emitter",function(e,t,n,r){!function(n,r){"function"==typeof define&&define.amd?define(r):"object"==typeof t?e.exports=r():r(n)}(this,function(e){"use strict";function t(e){return e?(e._events={},n(e,i)):void(this._events={})}function n(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e}function r(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&(t=t.concat(e[n]));return t}var i,o=Array.prototype.slice;return i=t.prototype,i.on=i.addEventListener=function(e,t){return(this._events[e]=this._events[e]||[]).push(t),this},i.one=i.once=function(e,t){var n=function(){this.off(e,n),t.apply(this,arguments)};return n.fn=t,this.on(e,n)},i.off=i.removeAllListners=i.removeEventListener=function(e,t){var n=this._events[e];if(0===arguments.length)this._events={};else if(1===arguments.length)delete this._events[e];else if(n){for(var r,i=0;i<n.length;i++)if(r=n[i],r===t||r.fn===t){n.splice(i,1);break}n.length||delete this._events[e]}return this},i.emit=i.trigger=function(e){var t=this._events[e];if(!t)return!1;for(var n=0,r=t.length;r>n;n++)t[n].apply(this,o.call(arguments,1));return!0},i.listeners=function(e){return e?this._events[e]?this._events[e]:[]:r(this._events)},e?void(e.Emitter=t):t})}),t.define("./index",function(e,n,r,i){var o;o=t("little-emitter/emitter"),function(){var e,t,n,r,i;return e=new o,n=function(e){var t,n;return n=document.getElementsByTagName(e),t=n[t.length-1]},i=n("script"),t=n("form"),r=function(){return t.getElementsByTagName("input")},document.addEventListener?document.addEventListener("DOMContentLoaded",r,!1):document.attachEvent&&document.attachEvent("onreadystatechange",function(){return"complete"===document.readyState?r():void 0}),window.addEventListener?window.addEventListener("load",r,!1):window.attachEvent&&window.attachEvent("onload",r),e}()}),t("./index")}).call(this,this);
+270
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
(function(e){function t(e,n){if({}.hasOwnProperty.call(t.cache,e))return t.cache[e];if("function"==typeof n)return void t.load(e,n);var i=t.resolve(e);if(!i)throw new Error("Failed to resolve module "+e);var r={id:e,require:t,filename:e,exports:{},loaded:!1,parent:null,children:[]},o=e.slice(0,e.lastIndexOf("/")+1);return t.cache[e]=r.exports,i.call(r.exports,r,r.exports,o,e),r.loaded=!0,t.cache[e]=r.exports}t.modules={},t.cache={},t.resolve=function(e){return{}.hasOwnProperty.call(t.modules,e)?t.modules[e]:void 0},t.define=function(e,n){t.modules[e]=n},t.define("little-emitter/emitter",function(e,t,n,i){!function(n,i){"function"==typeof define&&define.amd?define(i):"object"==typeof t?e.exports=i():i(n)}(this,function(e){"use strict";function t(e){return e?(e._events={},n(e,r)):void(this._events={})}function n(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e}function i(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&(t=t.concat(e[n]));return t}var r,o=Array.prototype.slice;return r=t.prototype,r.on=r.addEventListener=function(e,t){return(this._events[e]=this._events[e]||[]).push(t),this},r.one=r.once=function(e,t){var n=function(){this.off(e,n),t.apply(this,arguments)};return n.fn=t,this.on(e,n)},r.off=r.removeAllListners=r.removeEventListener=function(e,t){var n=this._events[e];if(0===arguments.length)this._events={};else if(1===arguments.length)delete this._events[e];else if(n){for(var i,r=0;r<n.length;r++)if(i=n[r],i===t||i.fn===t){n.splice(r,1);break}n.length||delete this._events[e]}return this},r.emit=r.trigger=function(e){var t=this._events[e];if(!t)return!1;for(var n=0,i=t.length;i>n;n++)t[n].apply(this,o.call(arguments,1));return!0},r.listeners=function(e){return e?this._events[e]?this._events[e]:[]:i(this._events)},e?void(e.Emitter=t):t})}),t.define("./index",function(e,n,i,r){var o,s;o=t("little-emitter/emitter"),s={InitScript:"init-inform-script",InitForm:"init-inform-form",InitInputs:"init-inform-inputs",InitSubmits:"init-inform-submits",OnSubmit:"inform-submit",OnDone:"inform-done"},function(){var e,t,n,i,r,u;return e=new o,e.Events=s,i=function(e){var t,n;return n=document.getElementsByTagName(e),t=n[n.length-1]},u=i("script"),n=i("form"),t=!0,r=function(){var i,r,o,a,c,f,l,d,m,v,h,p,g,w,b;if(t){for(t=!1,e.emit(s.InitScript,u),e.emit(s.InitForm,n),w=[],c=[],f=n.getElementsByTagName("input"),o=0,m=f.length;m>o;o++)a=f[o],"submit"!==a.getAttribute("type")?c.push(a):w.push(a);for(f=n.getElementsByTagName("select"),l=0,v=f.length;v>l;l++)a=f[l],c.push(a);for(e.emit(s.InitInputs,c),r=n.getElementsByTagName("button"),d=0,h=r.length;h>d;d++)i=r[d],b=i.getAttribute("type"),(null==b||"submit"===b)&&w.push(i);return e.emit(s.InitSubmits,w),p=function(){return e.emit(s.OnDone)},g=function(t){var i;return i=function(){return n.removeEventListener("submit",g),n.addEventListener("submit",p),n.dispatchEvent(new Event("submit",{bubbles:!1,cancelable:!0}))},e.emit(s.OnSubmit,i,t),t.preventDefault()},n.addEventListener("submit",g)}},document.addEventListener?document.addEventListener("DOMContentLoaded",r,!1):document.attachEvent&&document.attachEvent("onreadystatechange",function(){return"complete"===document.readyState?r():void 0}),"undefined"!=typeof window&&null!==window&&(window.Inform=e,window.Inform.events=s,window.addEventListener?window.addEventListener("load",r,!1):window.attachEvent&&window.attachEvent("onload",r)),e}()}),t("./index")}).call(this,this);
+5 -1
View File
@@ -11,6 +11,7 @@
"connect": "^3.4.0",
"executive": "^0.4.8",
"mocha": "^2.2.5",
"phantomjs": "^1.9.17",
"requisite": "^1.9.16",
"selenium-standalone": "^4.5.3",
"serve-static": "^1.10.0",
@@ -37,5 +38,8 @@
"bugs": {
"url": "https://github.com/crowdstart/inform/issues"
},
"homepage": "https://github.com/crowdstart/inform#readme"
"homepage": "https://github.com/crowdstart/inform#readme",
"dependencies": {
"little-emitter": "^0.1.3"
}
}
+401 -303
View File
@@ -1,324 +1,82 @@
Emitter = require 'little-emitter'
events =
InitScript: 'init-inform-script'
InitForm: 'init-inform-form'
InitInputs: 'init-inform-inputs'
InitSubmits: 'init-inform-submits'
OnSubmit: 'inform-submit'
OnDone: 'inform-done'
do ->
`var endpoint = "%s", ml = %s`
called = false
errors = null
forms = null
handlers = null
parent = null
script = null
selectors = {}
validate = false
XHR = ->
xhr = null
if window.XMLHttpRequest
xhr = new XMLHttpRequest()
else if window.ActiveXObject
xhr = new ActiveXObject 'Microsoft.XMLHTTP'
setHeaders: (headers) ->
for k,v of headers
xhr.setRequestHeader k, v
return
post: (url, headers, payload, cb) ->
xhr.open 'POST', url, true
@setHeaders headers
xhr.send payload
xhr.onreadystatechange = ->
if xhr.readyState == 4
if xhr.status == 200 or xhr.status == 201
cb null, xhr.status, xhr
else
cb (new Error 'Subscription failed'), xhr.status, xhr
return
return
# get form container
getContainer = (script, selector = '')->
if selector != ''
document.querySelector selectors.container
else
parent = script.parentNode
inputs = parent.getElementsByTagName 'input'
if inputs.length < 1
document.body
else
parent
emitter = new Emitter
emitter.Events = events
# get this script tag
getScript = ->
getLastTag = (selector)->
# find last script node
scripts = document.getElementsByTagName( 'script' )
tags = document.getElementsByTagName selector
# last element is this script tag
script = scripts[ scripts.length - 1 ]
script
# last element is this tag
tag = tags[tags.length - 1]
return tag
# Get elements from inside a parent
getElements = (parent, selector) ->
console.log 'getElements', parent, selector
script = getLastTag 'script'
form = getLastTag 'form'
if selector? and selector != ''
parent.querySelectorAll selector
first = true
init = ()->
if first
first = false
else
[parent]
# get value from a selector
getValue = (parent = document.body, selector) ->
el = parent.querySelector selector
el?.value?.trim()
# serialize a form
serializeForm = (el) ->
return {} unless el?
data =
metadata: {}
inputs = el.getElementsByTagName 'input'
# Loop over form elements
for input in inputs
# Clean up inputs
k = input.name.trim().toLowerCase()
v = input.value.trim()
# Skip inputs we don't care about
if k == '' or v == '' or (input.getAttribute 'type') == 'submit'
continue
# Detect emails
if /email/.test k
data.email = v
else
data.metadata[k] = v
# Use selectors if necessary
if selectors.email
data.email = getValue el, selectors.email
else
data.email ?= ''
for prop in ['firstname', 'lastname', 'name']
if (selector = selectors[prop])?
data.metadata[prop] = getValue el, selector
console.error 'Email is required' if data.email == ''
data
# get setting off script tag data attribute
attr = (name) ->
val = script.getAttribute 'data-' + name
return unless val?
switch val.trim().toLowerCase()
when 'false'
false
when 'true'
true
else
val
# Google event tracking code
google =
setup: ->
return if window.ga? or window._gaq?
((i, s, o, g, r, a, m) ->
i['GoogleAnalyticsObject'] = r
i[r] = i[r] or ->
(i[r].q = i[r].q or []).push arguments
return
i[r].l = 1 * new Date()
a = s.createElement(o)
m = s.getElementsByTagName(o)[0]
a.async = 1
a.src = g
m.parentNode.insertBefore a, m
return
) window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'
return
track: (opts) ->
return unless opts.category?
# you need to get the attributes off these things manually
emitter.emit events.InitScript, script
emitter.emit events.InitForm, form
google.setup()
category = opts.category ? 'Subscription'
action = opts.action ? opts.name ? 'Signup'
label = opts.label ? 'Lead'
value = opts.value ? 1
if window._gaq?
window._gaq.push ['_trackEvent', category, action, label, value]
if window.ga?
window.ga 'send', 'event', category, action, label, value
return
# Facebook event tracking
facebook =
setup: ->
return if window._fbq?.loaded
_fbq = window._fbq or (window._fbq = [])
fbds = document.createElement('script')
fbds.async = true
fbds.src = '//connect.facebook.net/en_US/fbds.js'
s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore fbds, s
_fbq.loaded = true
return
track: (opts) ->
return unless opts.id?
facebook.setup()
value = opts.value ? '1.00'
currency = opts.currency ? 'USD'
window._fbq.push ['track', opts.id,
value: value,
currency: currency,
]
return
# Trigger event tracking
track = ->
facebook.track ml.facebook
google.track ml.google
return
# Wire up submit handler
addHandler = (el, errorEl) ->
unless errorEl?
errorEl = document.createElement 'div'
errorEl.className = 'crowdstart-mailinglist-error'
errorEl.style.display = 'none'
errorEl.style.width = 100 + '%%'[0] # Prevents interpolation from picking up % as a thing needing interpolatin'
el.appendChild errorEl
showError = (msg) ->
errorEl.style.display = 'inline'
errorEl.innerHTML = msg
false
hideError = ->
errorEl.style.display = 'none'
thankYou = ->
switch ml.thankyou.type
when 'redirect'
setTimeout ->
window.location = ml.thankyou.url
, 1000
when 'html'
el.innerHTML = ml.thankyou.html
if document.createEvent && document.dispatchEvent
event = document.createEvent 'Event'
event.initEvent 'thankyou', true, true
document.dispatchEvent event
submits = []
inputs = []
ins = form.getElementsByTagName 'input'
for input in ins
if input.getAttribute('type') != 'submit'
inputs.push input
else
console.log "Could not create or dispatch thankyou event"
submits.push input
submitHandler = (ev) ->
if ev.defaultPrevented
return
else
ev.preventDefault()
ins = form.getElementsByTagName 'select'
for input in ins
inputs.push input
data = serializeForm el
console.log data
emitter.emit events.InitInputs, inputs
if ml.validate
unless data.email?
return showError 'Email is required'
if (data.email.indexOf '@') == -1
return showError 'Invalid email'
if data.email.length < 3
return showError 'Invalid email'
hideError()
buttons = form.getElementsByTagName 'button'
for button in buttons
type = button.getAttribute('type')
if !type? || type == 'submit'
submits.push(button)
payload = JSON.stringify data
emitter.emit events.InitSubmits, submits
headers =
'X-Requested-With': 'XMLHttpRequest',
'Content-type': 'application/json; charset=utf-8'
# Called on successful submit
ondone = ()->
emitter.emit events.OnDone
xhr = XHR()
xhr.post endpoint, headers, payload, (err, status, xhr) ->
return thankYou() if status == 409
return showError(err) if err?
# Called when trying to submit
onsubmit = (event)->
done = ()->
form.removeEventListener 'submit', onsubmit
form.addEventListener 'submit', ondone
# Fire tracking pixels
track()
thankYou()
false
(ev) ->
el.removeEventListener 'submit', addHandler
el.addEventListener 'submit', submitHandler
setTimeout ->
el.dispatchEvent new Event 'submit',
form.dispatchEvent new Event 'submit',
bubbles: false
cancelable: true
, 500
ev.preventDefault()
false
# Call done to manually fire
emitter.emit events.OnSubmit, done, event
event.preventDefault()
# Init all the things
init = ->
if called then return else called = true
props = ['container', 'forms', 'submits', 'errors', 'email', 'firstname', 'lastname', 'name']
for prop in props
selectors[prop] = (attr prop) ? ml.selectors?[prop]
# default selector for submit button
selectors.submits ?= 'input[type="submit"]'
# are we validating?
ml.validate ?= (attr 'validate') ? false
parent = getContainer script, selectors.container
forms = getElements parent, selectors.forms
handlers = getElements parent, selectors.submits
# find error divs
if selectors.errors
errors = getElements parent, selectors.errors
else
errors = []
console.log 'selectors', selectors
console.log 'forms', forms
console.log 'handlers', handlers
console.log 'errors', errors
for handler, i in handlers
do (handler, i) ->
return if handler.getAttribute 'data-hijacked'
handler.setAttribute 'data-hijacked', true
handler.addEventListener 'click', (addHandler forms[i], errors[i])
handler.addEventListener 'submit', (addHandler forms[i], errors[i])
# Get script tag, has to run before rest of DOM loads
script = getScript()
form.addEventListener 'submit', onsubmit
# Run init after DOM loads, attach various listeners
if document.addEventListener
@@ -327,9 +85,349 @@ do ->
document.attachEvent 'onreadystatechange', ->
init() if document.readyState == 'complete'
if window.addEventListener
window.addEventListener 'load', init, false
else if window.attachEvent
window.attachEvent 'onload', init
if window?
window.Inform = emitter
window.Inform.events = events
return
if window.addEventListener
window.addEventListener 'load', init, false
else if window.attachEvent
window.attachEvent 'onload', init
return emitter
# do ->
# `var endpoint = "%s", ml = %s`
# called = false
# errors = null
# forms = null
# handlers = null
# parent = null
# script = null
# selectors = {}
# validate = false
# XHR = ->
# xhr = null
# if window.XMLHttpRequest
# xhr = new XMLHttpRequest()
# else if window.ActiveXObject
# xhr = new ActiveXObject 'Microsoft.XMLHTTP'
# setHeaders: (headers) ->
# for k,v of headers
# xhr.setRequestHeader k, v
# return
# post: (url, headers, payload, cb) ->
# xhr.open 'POST', url, true
# @setHeaders headers
# xhr.send payload
# xhr.onreadystatechange = ->
# if xhr.readyState == 4
# if xhr.status == 200 or xhr.status == 201
# cb null, xhr.status, xhr
# else
# cb (new Error 'Subscription failed'), xhr.status, xhr
# return
# return
# # get form container
# getContainer = (script, selector = '')->
# if selector != ''
# document.querySelector selectors.container
# else
# parent = script.parentNode
# inputs = parent.getElementsByTagName 'input'
# if inputs.length < 1
# document.body
# else
# parent
# # get this script tag
# getScript = ->
# # find last script node
# scripts = document.getElementsByTagName( 'script' )
# # last element is this script tag
# script = scripts[ scripts.length - 1 ]
# script
# # Get elements from inside a parent
# getElements = (parent, selector) ->
# console.log 'getElements', parent, selector
# if selector? and selector != ''
# parent.querySelectorAll selector
# else
# [parent]
# # get value from a selector
# getValue = (parent = document.body, selector) ->
# el = parent.querySelector selector
# el?.value?.trim()
# # serialize a form
# serializeForm = (el) ->
# return {} unless el?
# data =
# metadata: {}
# inputs = el.getElementsByTagName 'input'
# # Loop over form elements
# for input in inputs
# # Clean up inputs
# k = input.name.trim().toLowerCase()
# v = input.value.trim()
# # Skip inputs we don't care about
# if k == '' or v == '' or (input.getAttribute 'type') == 'submit'
# continue
# # Detect emails
# if /email/.test k
# data.email = v
# else
# data.metadata[k] = v
# # Use selectors if necessary
# if selectors.email
# data.email = getValue el, selectors.email
# else
# data.email ?= ''
# for prop in ['firstname', 'lastname', 'name']
# if (selector = selectors[prop])?
# data.metadata[prop] = getValue el, selector
# console.error 'Email is required' if data.email == ''
# data
# # get setting off script tag data attribute
# attr = (name) ->
# val = script.getAttribute 'data-' + name
# return unless val?
# switch val.trim().toLowerCase()
# when 'false'
# false
# when 'true'
# true
# else
# val
# # Google event tracking code
# google =
# setup: ->
# return if window.ga? or window._gaq?
# ((i, s, o, g, r, a, m) ->
# i['GoogleAnalyticsObject'] = r
# i[r] = i[r] or ->
# (i[r].q = i[r].q or []).push arguments
# return
# i[r].l = 1 * new Date()
# a = s.createElement(o)
# m = s.getElementsByTagName(o)[0]
# a.async = 1
# a.src = g
# m.parentNode.insertBefore a, m
# return
# ) window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'
# return
# track: (opts) ->
# return unless opts.category?
# google.setup()
# category = opts.category ? 'Subscription'
# action = opts.action ? opts.name ? 'Signup'
# label = opts.label ? 'Lead'
# value = opts.value ? 1
# if window._gaq?
# window._gaq.push ['_trackEvent', category, action, label, value]
# if window.ga?
# window.ga 'send', 'event', category, action, label, value
# return
# # Facebook event tracking
# facebook =
# setup: ->
# return if window._fbq?.loaded
# _fbq = window._fbq or (window._fbq = [])
# fbds = document.createElement('script')
# fbds.async = true
# fbds.src = '//connect.facebook.net/en_US/fbds.js'
# s = document.getElementsByTagName('script')[0]
# s.parentNode.insertBefore fbds, s
# _fbq.loaded = true
# return
# track: (opts) ->
# return unless opts.id?
# facebook.setup()
# value = opts.value ? '1.00'
# currency = opts.currency ? 'USD'
# window._fbq.push ['track', opts.id,
# value: value,
# currency: currency,
# ]
# return
# # Trigger event tracking
# track = ->
# facebook.track ml.facebook
# google.track ml.google
# return
# # Wire up submit handler
# addHandler = (el, errorEl) ->
# unless errorEl?
# errorEl = document.createElement 'div'
# errorEl.className = 'crowdstart-mailinglist-error'
# errorEl.style.display = 'none'
# errorEl.style.width = 100 + '%%'[0] # Prevents interpolation from picking up % as a thing needing interpolatin'
# el.appendChild errorEl
# showError = (msg) ->
# errorEl.style.display = 'inline'
# errorEl.innerHTML = msg
# false
# hideError = ->
# errorEl.style.display = 'none'
# thankYou = ->
# switch ml.thankyou.type
# when 'redirect'
# setTimeout ->
# window.location = ml.thankyou.url
# , 1000
# when 'html'
# el.innerHTML = ml.thankyou.html
# if document.createEvent && document.dispatchEvent
# event = document.createEvent 'Event'
# event.initEvent 'thankyou', true, true
# document.dispatchEvent event
# else
# console.log "Could not create or dispatch thankyou event"
# submitHandler = (ev) ->
# if ev.defaultPrevented
# return
# else
# ev.preventDefault()
# data = serializeForm el
# console.log data
# if ml.validate
# unless data.email?
# return showError 'Email is required'
# if (data.email.indexOf '@') == -1
# return showError 'Invalid email'
# if data.email.length < 3
# return showError 'Invalid email'
# hideError()
# payload = JSON.stringify data
# headers =
# 'X-Requested-With': 'XMLHttpRequest',
# 'Content-type': 'application/json; charset=utf-8'
# xhr = XHR()
# xhr.post endpoint, headers, payload, (err, status, xhr) ->
# return thankYou() if status == 409
# return showError(err) if err?
# # Fire tracking pixels
# track()
# thankYou()
# false
# (ev) ->
# el.removeEventListener 'submit', addHandler
# el.addEventListener 'submit', submitHandler
# setTimeout ->
# el.dispatchEvent new Event 'submit',
# bubbles: false
# cancelable: true
# , 500
# ev.preventDefault()
# false
# # Init all the things
# init = ->
# if called then return else called = true
# props = ['container', 'forms', 'submits', 'errors', 'email', 'firstname', 'lastname', 'name']
# for prop in props
# selectors[prop] = (attr prop) ? ml.selectors?[prop]
# # default selector for submit button
# selectors.submits ?= 'input[type="submit"]'
# # are we validating?
# ml.validate ?= (attr 'validate') ? false
# parent = getContainer script, selectors.container
# forms = getElements parent, selectors.forms
# handlers = getElements parent, selectors.submits
# # find error divs
# if selectors.errors
# errors = getElements parent, selectors.errors
# else
# errors = []
# console.log 'selectors', selectors
# console.log 'forms', forms
# console.log 'handlers', handlers
# console.log 'errors', errors
# for handler, i in handlers
# do (handler, i) ->
# return if handler.getAttribute 'data-hijacked'
# handler.setAttribute 'data-hijacked', true
# handler.addEventListener 'click', (addHandler forms[i], errors[i])
# handler.addEventListener 'submit', (addHandler forms[i], errors[i])
# # Get script tag, has to run before rest of DOM loads
# script = getScript()
# # Run init after DOM loads, attach various listeners
# if document.addEventListener
# document.addEventListener 'DOMContentLoaded', init, false
# else if document.attachEvent
# document.attachEvent 'onreadystatechange', ->
# init() if document.readyState == 'complete'
# if window.addEventListener
# window.addEventListener 'load', init, false
# else if window.attachEvent
# window.attachEvent 'onload', init
# return
+18 -18
View File
@@ -1,22 +1,22 @@
module.exports = [
browserName: 'iphone'
platform: 'OS X 10.10'
version: '8.2'
deviceName: 'iPhone Simulator'
deviceOrientation: 'portrait'
,
browserName: 'iphone'
platform: 'OS X 10.10'
version: '7.1'
deviceName: 'iPhone Simulator'
deviceOrientation: 'portrait'
,
browserName: 'iphone'
platform: 'OS X 10.10'
version: '6.1'
deviceName: 'iPhone Simulator'
deviceOrientation: 'portrait'
,
# browserName: 'iphone'
# platform: 'OS X 10.10'
# version: '8.2'
# deviceName: 'iPhone Simulator'
# deviceOrientation: 'portrait'
# ,
# browserName: 'iphone'
# platform: 'OS X 10.10'
# version: '7.1'
# deviceName: 'iPhone Simulator'
# deviceOrientation: 'portrait'
# ,
# browserName: 'iphone'
# platform: 'OS X 10.10'
# version: '6.1'
# deviceName: 'iPhone Simulator'
# deviceOrientation: 'portrait'
# ,
browserName: 'android'
platform: 'Linux'
version: '5.1'
+1
View File
@@ -0,0 +1 @@
../inform.js
+5
View File
File diff suppressed because one or more lines are too long
+62 -2
View File
@@ -7,5 +7,65 @@ describe "Inform (#{process.env.BROWSER})", ->
browser = getBrowser()
testPage = "http://localhost:#{process.env.PORT ? 3333}/test.html"
it 'Should load', ->
require '../src'
it 'should detect the correct script/form', (done)->
browser
.url testPage
.waitForExist "#lastthing"
.getText '#script', (err, res) ->
res.should.equal 'inform'
.getText '#form', (err, res) ->
res.should.equal 'test'
.getText '#inputs', (err, res) ->
res.should.equal 'email: testuser@email.com, name: Test User, misc: Something'
.getText '#submits', (err, res) ->
res.should.equal 's, as, aas'
.call done
#dispatchEvent doesn't seem to work
if process.env.BROWSER != 'phantomjs'
it 'should submit and done for submit input', (done)->
browser
.url testPage
.waitForExist "#lastthing"
.click '#s'
.getText '#submited', (err, res) ->
res.should.equal 'true'
.getText '#doned', (err, res) ->
res.should.equal 'true'
.call done
it 'should submit and done for untyped button', (done)->
browser
.url testPage
.waitForExist "#lastthing"
.click '#as'
# yes we actually need this as there are delays in the code
.getText '#submited', (err, res) ->
res.should.equal 'true'
.getText '#doned', (err, res) ->
res.should.equal 'true'
.call done
it 'should submit and done for submit button', (done)->
browser
.url testPage
.waitForExist "#lastthing"
.click '#aas'
# yes we actually need this as there are delays in the code
.getText '#submited', (err, res) ->
res.should.equal 'true'
.getText '#doned', (err, res) ->
res.should.equal 'true'
.call done
it 'should not submit or done for other button', (done)->
browser
.url testPage
.waitForExist "#lastthing"
.click '#r'
# yes we actually need this as there are delays in the code
.getText '#submited', (err, res) ->
res.should.equal 'false'
.getText '#doned', (err, res) ->
res.should.equal 'false'
.call done
+73
View File
@@ -0,0 +1,73 @@
<!doctype html>
<html class="no-js" lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<script src="jquery-1.11.3.min.js"></script>
</head>
<body>
<form id="wrongform">
</form>
<form id="test">
<script id="inform" src="inform.js"></script>
<input id="email" value="testuser@email.com"></input>
<input id="name" value="Test User"></input>
<input id="misc" value="Something"></input>
<input id = "s" type="submit"></input>
<button id="as">Also Submit</button>
<button id="aas" type="submit">Also Also Submit</button>
<button id="r" type="reset">Reset</button>
</form>
<form id="alsowrongform">
</form>
<div id="script">
</div>
<div id="form">
</div>
<div id="inputs">
</div>
<div id="submits">
</div>
<div id="submited">false</div>
<div id="doned">false</div>
<script id="lastthing">
var Inform = window.Inform
Inform.once(Inform.Events.InitScript, function(el){
$('#script').html(el.id)
})
Inform.once(Inform.Events.InitForm, function(el){
$('#form').html(el.id)
})
Inform.once(Inform.Events.InitInputs, function(els){
$('#inputs').html(els.map(function(el){
$el = $(el)
return $el.attr('id') + ': ' + $el.val()
}).join(', '))
})
Inform.once(Inform.Events.InitSubmits, function(els){
$('#submits').html(els.map(function(el){
return el.id
}).join(', '))
})
Inform.once(Inform.Events.OnSubmit, function(done){
$('#submited').html('true')
done()
})
Inform.once(Inform.Events.OnDone, function(){
$('#doned').html('true')
})
</script>
</body>
</html>
+4 -1
View File
@@ -2,11 +2,14 @@ webdriver = require 'webdriverio'
exports.getBrowser = ->
caps =
browserName: process.env.BROWSER ? 'phantomjs'
#dispatch event no worky in phantomjs
browserName: 'chrome'
platform: process.env.PLATFORM
version: process.env.VERSION
deviceName: process.env.DEVICE_NAME
deviceOrientation: process.env.DEVICE_ORIENTATION
'phantomjs.binary.path': './node_modules/phantomjs/bin/phantomjs'
logLevel: 'verbose'
opts =
desiredCapabilities: caps