new style api

This commit is contained in:
Jaime Pillora
2013-06-03 12:28:03 +10:00
parent d0838b4c6d
commit 37631df657
3 changed files with 344 additions and 303 deletions
+219 -188
View File
@@ -3,8 +3,11 @@
#plugin constants
pluginName = 'notify'
className = '__'+pluginName
className = pluginName+'js'
# ================================
# POSITIONING
# ================================
positions =
t: 'top'
@@ -32,109 +35,144 @@ parsePosition = (str) ->
pos.push w if positions[w]
pos
#built-in styles
styles =
core:
html: """
<div class="#{className}Wrapper">
<div class="#{className}Arrow"></div>
<div class="#{className}Container"></div>
</div>
# ================================
# STYLES
# ================================
styles = {}
coreStyle =
name: 'core'
html: """
<div class="#{className}-wrapper">
<div class="#{className}-arrow"></div>
<div class="#{className}-container"></div>
</div>
"""
# <div class="#{className}Debug"></div>
# .#{className}Debug {
# position: absolute;
# border: 3px solid red;
# height: 0;
# width: 0;
# }
css: """
.#{className}-corner {
position: fixed;
top: 0;
right: 0;
margin: 5px;
z-index: 1050;
}
.#{className}-corner .#{className}-wrapper,
.#{className}-corner .#{className}-container {
position: relative;
display: block;
height: inherit;
width: inherit;
}
.#{className}-wrapper {
z-index: 1;
position: absolute;
display: inline-block;
height: 0;
width: 0;
}
.#{className}-container {
display: none;
z-index: 1;
position: absolute;
cursor: pointer;
}
.#{className}-text {
position: relative;
}
.#{className}-arrow {
position: absolute;
z-index: 2;
width: 0;
height: 0;
}
"""
defaultStyleName = "bootstrap"
defaultStyle =
html: """
<div>
<span data-notify-text></span>
</div>
"""
classes:
base: """
padding: 8px 35px 8px 14px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
background-color: #fcf8e3;
border: 1px solid #fbeed5;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
white-space: nowrap;
"""
error: """
color: #B94A48;
background-color: #F2DEDE;
border-color: #EED3D7;
padding-left: 25px;
background-repeat: no-repeat;
background-position: 3px 7px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtRJREFUeNqkVc1u00AQHq+dOD+0poIQfkIjalW0SEGqRMuRnHos3DjwAH0ArlyQeANOOSMeAA5VjyBxKBQhgSpVUKKQNGloFdw4cWw2jtfMOna6JOUArDTazXi/b3dm55socPqQhFka++aHBsI8GsopRJERNFlY88FCEk9Yiwf8RhgRyaHFQpPHCDmZG5oX2ui2yilkcTT1AcDsbYC1NMAyOi7zTX2Agx7A9luAl88BauiiQ/cJaZQfIpAlngDcvZZMrl8vFPK5+XktrWlx3/ehZ5r9+t6e+WVnp1pxnNIjgBe4/6dAysQc8dsmHwPcW9C0h3fW1hans1ltwJhy0GxK7XZbUlMp5Ww2eyan6+ft/f2FAqXGK4CvQk5HueFz7D6GOZtIrK+srupdx1GRBBqNBtzc2AiMr7nPplRdKhb1q6q6zjFhrklEFOUutoQ50xcX86ZlqaZpQrfbBdu2R6/G19zX6XSgh6RX5ubyHCM8nqSID6ICrGiZjGYYxojEsiw4PDwMSL5VKsC8Yf4VRYFzMzMaxwjlJSlCyAQ9l0CW44PBADzXhe7xMdi9HtTrdYjFYkDQL0cn4Xdq2/EAE+InCnvADTf2eah4Sx9vExQjkqXT6aAERICMewd/UAp/IeYANM2joxt+q5VI+ieq2i0Wg3l6DNzHwTERPgo1ko7XBXj3vdlsT2F+UuhIhYkp7u7CarkcrFOCtR3H5JiwbAIeImjT/YQKKBtGjRFCU5IUgFRe7fF4cCNVIPMYo3VKqxwjyNAXNepuopyqnld602qVsfRpEkkz+GFL1wPj6ySXBpJtWVa5xlhpcyhBNwpZHmtX8AGgfIExo0ZpzkWVTBGiXCSEaHh62/PoR0p/vHaczxXGnj4bSo+G78lELU80h1uogBwWLf5YlsPmgDEd4M236xjm+8nm4IuE/9u+/PH2JXZfbwz4zw1WbO+SQPpXfwG/BBgAhCNZiSb/pOQAAAAASUVORK5CYII=);
"""
info: ""
warn: ""
addStyle = (name, def) ->
if styles[name]
alert "#{pluginName}: style '#{name}' already defined"
return
def.name = name
styles[name] = def
return if def.cssElem
cssText = ""
if def.classes
$.each def.classes, (subClassName, css) ->
cssText += """
.#{className}-#{def.name}-#{subClassName} {
#{css}
}\n
"""
if def.css
cssText += """
/* styles for #{def.name} */
#{def.css}
"""
# <div class="#{className}Debug"></div>
return unless cssText
def.cssElem = insertCSS cssText
def.cssElem.attr('data-notify-style', def.name)
# .#{className}Debug {
# position: absolute;
# border: 3px solid red;
# height: 0;
# width: 0;
# }
insertCSS = (cssText) ->
elem = createElem("style")
$("head").append elem
try
elem.html cssText
catch e #ie fix
elem[0].styleSheet.cssText = cssText
elem
css: """
.#{className}Corner {
position: fixed;
top: 0;
right: 0;
margin: 5px;
z-index: 1050;
}
.#{className}Corner .#{className}Wrapper,
.#{className}Corner .#{className}Container {
position: relative;
display: block;
height: inherit;
width: inherit;
}
.#{className}Wrapper {
z-index: 1;
position: absolute;
display: inline-block;
height: 0;
width: 0;
}
.#{className}Container {
display: none;
z-index: 1;
position: absolute;
cursor: pointer;
}
.#{className}Text {
position: relative;
}
.#{className}Arrow {
position: absolute;
z-index: 2;
width: 0;
height: 0;
}
"""
user:
bootstrap:
html: """
<div>
<span data-notify-text></span>
</div>
"""
classes:
base: """
padding: 8px 35px 8px 14px;
margin-bottom: 20px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
background-color: #fcf8e3;
border: 1px solid #fbeed5;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
"""
error:
info:
warn:
bootstrap:
html: """
<div class="alert alert-error #{className}Bootstrap">
<strong data-notify-text></strong>
</div>
"""
css: """
.#{className}Bootstrap {
white-space: nowrap;
margin: 5px !important;
padding-left: 25px !important;
background-repeat: no-repeat;
background-position: 3px 7px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtRJREFUeNqkVc1u00AQHq+dOD+0poIQfkIjalW0SEGqRMuRnHos3DjwAH0ArlyQeANOOSMeAA5VjyBxKBQhgSpVUKKQNGloFdw4cWw2jtfMOna6JOUArDTazXi/b3dm55socPqQhFka++aHBsI8GsopRJERNFlY88FCEk9Yiwf8RhgRyaHFQpPHCDmZG5oX2ui2yilkcTT1AcDsbYC1NMAyOi7zTX2Agx7A9luAl88BauiiQ/cJaZQfIpAlngDcvZZMrl8vFPK5+XktrWlx3/ehZ5r9+t6e+WVnp1pxnNIjgBe4/6dAysQc8dsmHwPcW9C0h3fW1hans1ltwJhy0GxK7XZbUlMp5Ww2eyan6+ft/f2FAqXGK4CvQk5HueFz7D6GOZtIrK+srupdx1GRBBqNBtzc2AiMr7nPplRdKhb1q6q6zjFhrklEFOUutoQ50xcX86ZlqaZpQrfbBdu2R6/G19zX6XSgh6RX5ubyHCM8nqSID6ICrGiZjGYYxojEsiw4PDwMSL5VKsC8Yf4VRYFzMzMaxwjlJSlCyAQ9l0CW44PBADzXhe7xMdi9HtTrdYjFYkDQL0cn4Xdq2/EAE+InCnvADTf2eah4Sx9vExQjkqXT6aAERICMewd/UAp/IeYANM2joxt+q5VI+ieq2i0Wg3l6DNzHwTERPgo1ko7XBXj3vdlsT2F+UuhIhYkp7u7CarkcrFOCtR3H5JiwbAIeImjT/YQKKBtGjRFCU5IUgFRe7fF4cCNVIPMYo3VKqxwjyNAXNepuopyqnld602qVsfRpEkkz+GFL1wPj6ySXBpJtWVa5xlhpcyhBNwpZHmtX8AGgfIExo0ZpzkWVTBGiXCSEaHh62/PoR0p/vHaczxXGnj4bSo+G78lELU80h1uogBwWLf5YlsPmgDEd4M236xjm+8nm4IuE/9u+/PH2JXZfbwz4zw1WbO+SQPpXfwG/BBgAhCNZiSb/pOQAAAAASUVORK5CYII=);
}
"""
colors:
red: '#eed3d7'
# ================================
# OPTIONS
# ================================
#overridable options
pluginOptions =
@@ -142,17 +180,11 @@ pluginOptions =
autoHideDelay: 2000
arrowShow: true
arrowSize: 5
arrowColor: 'border'
position: 'bottom'
# Default style
style: 'default'
# Default color
color: 'black'
# Color mappings
colors:
red: '#b94a48'
green: '#33be40'
black: '#393939'
blue: '#00f'
style: 'bootstrap'
cls: 'error'
showAnimation: 'slideDown'
showDuration: 400
hideAnimation: 'slideUp'
@@ -163,16 +195,25 @@ pluginOptions =
#TODO add z-index watches
#parents: { '.ui-dialog': 5001 }
# plugin helpers
createElem = (tag) ->
$ "<#{tag}></#{tag}>"
inherit = (a, b) ->
F = () ->
F.prototype = a
$.extend true, new F(), b
options = (opts) ->
$.extend pluginOptions, opts
# ================================
# DOM MANIPULATION
# ================================
# plugin helpers
createElem = (tag) ->
$ "<#{tag}></#{tag}>"
# container for element-less notifications
cornerElem = createElem("div").addClass("#{className}Corner")
cornerElem = createElem("div").addClass("#{className}-corner")
#gets first on n radios, and gets the fancy stylised input for hidden inputs
getAnchorElement = (element) ->
@@ -216,18 +257,10 @@ realign = (alignment, inner, outer) ->
outer - inner
throw "Invalid alignment"
insertCSS = (style) ->
return unless style and style.css
elem = style.cssElem
return if elem
elem = createElem("style")
$("head").append elem
style.cssElem = elem
try
elem.html style.css
catch e #ie fix
elem[0].styleSheet.cssText = style.css
# ================================
# NOTIFY CLASS
# ================================
#define plugin
class Notification
@@ -237,15 +270,13 @@ class Notification
options = {color: options} if typeof options is 'string'
@options = inherit pluginOptions, if $.isPlainObject(options) then options else {}
#load user css into dom
@loadCSS()
#load user html into @userContainer
@loadHTML()
@wrapper = $(styles.core.html)
@wrapper = $(coreStyle.html)
@wrapper.data className, @
@arrow = @wrapper.find ".#{className}Arrow"
@container = @wrapper.find ".#{className}Container"
@arrow = @wrapper.find ".#{className}-arrow"
@container = @wrapper.find ".#{className}-container"
@container.append @userContainer
if elem and elem.length
@@ -263,20 +294,13 @@ class Notification
@container.hide()
@run(data)
loadCSS: (style) ->
unless style
name = @options.style
style = @getStyle(name)
#insert
insertCSS style
loadHTML: ->
style = @getStyle()
@userContainer = $(style.html)
@text = @userContainer.find '[data-notify-text]'
if @text.length is 0
throw "style: #{name} HTML is missing the: 'data-notify-text' attribute"
@text.addClass "#{className}Text"
@text.addClass "#{className}-text"
show: (show, callback = $.noop) ->
@@ -318,6 +342,8 @@ class Notification
#get user defined position
position = @getPosition()
console.log position
pMain = position[0]
pAlign = position[1]
pArrow = position[2] or pAlign
@@ -347,6 +373,10 @@ class Notification
incr css, 'top', @options.offsetY
incr css, 'left', @options.offsetX
style = @getStyle()
# if style.css
#calculate arrow
if not @options.arrowShow
@arrow.hide()
@@ -357,7 +387,7 @@ class Notification
for pos in mainPositions
posFull = positions[pos]
continue if pos is opp
color = if posFull is mainFull then @getColor() else 'transparent'
color = if posFull is mainFull then 'black' else 'transparent'
arrowCss["border-#{posFull}"] = "#{arrowSize}px solid #{color}"
#add some room for the arrow
incr css, positions[opp], arrowSize
@@ -385,7 +415,7 @@ class Notification
pos[0] = 'b' if pos.length is 0
if pos[0] not in mainPositions
throw "Must be one of [#{mainPositions}]"
#validate alignment
if pos.length is 1 or
(pos[0] in vAligns and pos[1] not in hAligns) or
@@ -400,27 +430,26 @@ class Notification
# return 'bottom'
throw "Not implemented"
getColor: ->
styleColors = @getStyle().colors
return (styleColors and
styleColors[@options.color]) or
@options.colors[@options.color] or
@options.color
getStyle: (name) ->
name = @options.style unless name
name = 'default' unless name
style = styles.user[name]
style = styles[name]
throw "Missing style: #{name}" unless style
style
updateStyle: ->
#update colors
@wrapper.find('[data-notify-style]').each (i,e) =>
$(e).attr 'style', $(e).attr('data-notify-style').
replace(/\{\{\s*color\s*\}\}/ig, @getColor())
# .
# replace(/\{\{\s*position\s*\}\}/ig, @getPosition())
updateStyleClasses: ->
cls = ['base']
if $.isArray @options.cls
cls = cls.concat @options.cls
else if @options.cls
cls.push @options.cls
style = @getStyle()
cls = $.map(cls, (n) -> "#{className}-#{style.name}-#{n}").join ' '
@userContainer.attr 'class', cls
#run plugin
run: (data, options) ->
@@ -443,6 +472,10 @@ class Notification
else
@text.empty().append(data)
#set styles
@updateStyleClasses()
#position
@updatePosition()
@show true
@@ -458,6 +491,34 @@ class Notification
@show false, =>
@wrapper.remove()
# ================================
# API
# ================================
# $.pluginName( { ... } ) changes options for all instances
$[pluginName] = (elem, data, options) ->
if (elem and elem.nodeName) or elem.jquery
$(elem)[pluginName](data, options)
else
options = data
data = elem
new Notification null, data, options
elem
# $( ... ).pluginName( { .. } ) creates a cached instance on each
$.fn[pluginName] = (data, options) ->
$(@).each ->
inst = getAnchorElement($(@)).data(className)
if inst
inst.run data, options
else
new Notification $(@), data, options
@
#extra
$.extend $[pluginName], { options, addStyle }
#when ready
$ ->
#corner notification container
@@ -470,11 +531,13 @@ $ ->
bootstrapDetected = true
return false
#add core styles
insertCSS styles.core
#insert default style
insertCSS(coreStyle.css).attr('data-notify-style', 'core')
#insert default style
$[pluginName].addStyle(defaultStyleName, defaultStyle)
#watch all notifications clicks
$(document).on 'click', ".#{className}Wrapper", ->
$(document).on 'click', ".#{className}-wrapper", ->
inst = $(@).data className
return unless inst
if inst.elem
@@ -482,35 +545,3 @@ $ ->
else
inst.destroy()
# publicise jquery plugin
# return alert "$.#{pluginName} already defined" if $[pluginName]?
# $.pluginName( { ... } ) changes options for all instances
$[pluginName] = (elem, data, options) ->
if (elem and elem.nodeName) or elem.jquery
$(elem)[pluginName](data, options)
else
options = data
data = elem
new Notification null, data, options
elem
# publicise methods
$[pluginName].options = (options) ->
$.extend pluginOptions, options
$[pluginName].styles = (s) ->
$.extend true, styles.user, s
$[pluginName].insertCSS = insertCSS
# $( ... ).pluginName( { .. } ) creates a cached instance on each
$.fn[pluginName] = (data, options) ->
$(@).each ->
inst = getAnchorElement($(@)).data(className)
if inst
inst.run data, options
else
new Notification $(@), data, options
@