work in progress, positioning

This commit is contained in:
Jaime Pillora
2013-05-31 13:18:43 +10:00
parent aa7d387776
commit 930f9903c5
4 changed files with 184 additions and 149 deletions
+87 -71
View File
@@ -1,11 +1,12 @@
/** Notify.js - v0.0.1 - 2013/05/30 /** Notify.js - v0.0.1 - 2013/05/31
* http://notifyjs.com/ * http://notifyjs.com/
* Copyright (c) 2013 Jaime Pillora - MIT * Copyright (c) 2013 Jaime Pillora - MIT
*/ */
(function(window,document,undefined) { (function(window,document,undefined) {
'use strict'; 'use strict';
var Notification, className, cornerElem, createElem, getAnchorElement, incr, inherit, insertCSS, opposites, parsePosition, pluginName, pluginOptions, positions, styles; var Notification, className, cornerElem, createElem, getAnchorElement, hPositions, incr, inherit, insertCSS, mainPositions, opposites, parsePosition, pluginName, pluginOptions, positions, realign, styles, vPositions,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
pluginName = 'notify'; pluginName = 'notify';
@@ -20,6 +21,12 @@ positions = {
r: 'right' r: 'right'
}; };
hPositions = ['l', 'c', 'r'];
vPositions = ['t', 'm', 'b'];
mainPositions = ['t', 'b', 'l', 'r'];
opposites = { opposites = {
t: 'b', t: 'b',
m: null, m: null,
@@ -44,8 +51,8 @@ parsePosition = function(str) {
styles = { styles = {
core: { core: {
html: "<div class=\"" + className + "Wrapper\">\n <div class=\"" + className + "Arrow\"></div>\n <div class=\"" + className + "Container\"></div>\n</div>", html: "<div class=\"" + className + "Wrapper\">\n <div class=\"" + className + "Debug\"></div>\n <div class=\"" + className + "Arrow\"></div>\n <div class=\"" + className + "Container\"></div>\n</div>",
css: "." + className + "Corner {\n position: fixed;\n top: 0;\n right: 0;\n margin: 5px;\n z-index: 1050;\n}\n\n." + className + "Corner ." + className + "Wrapper,\n." + className + "Corner ." + className + "Container {\n position: relative;\n display: block;\n height: inherit;\n width: inherit;\n}\n\n." + className + "Wrapper {\n z-index: 1;\n position: absolute;\n display: inline-block;\n height: 0;\n width: 0;\n border: thin solid red;\n}\n\n." + className + "Container {\n display: none;\n z-index: 1;\n position: absolute;\n cursor: pointer;\n}\n\n." + className + "Text {\n position: relative;\n}\n\n." + className + "Arrow {\n margin-top: 2px;\n position: absolute;\n z-index: 2;\n margin-left: 10px;\n width: 0;\n height: 0;\n}\n" css: "." + className + "Corner {\n position: fixed;\n top: 0;\n right: 0;\n margin: 5px;\n z-index: 1050;\n}\n\n." + className + "Corner ." + className + "Wrapper,\n." + className + "Corner ." + className + "Container {\n position: relative;\n display: block;\n height: inherit;\n width: inherit;\n}\n\n." + className + "Wrapper {\n z-index: 1;\n position: absolute;\n display: inline-block;\n height: 0;\n width: 0;\n}\n\n." + className + "Debug {\n position: absolute;\n border: 3px solid red;\n height: 0;\n width: 0;\n}\n\n." + className + "Container {\n display: none;\n z-index: 1;\n position: absolute;\n cursor: pointer;\n}\n\n." + className + "Text {\n position: relative;\n}\n\n." + className + "Arrow {\n position: absolute;\n z-index: 2;\n width: 0;\n height: 0;\n}\n"
}, },
user: { user: {
"default": { "default": {
@@ -65,7 +72,7 @@ styles = {
pluginOptions = { pluginOptions = {
autoHide: false, autoHide: false,
autoHideDelay: 2000, autoHideDelay: 2000,
arrowShow: false, arrowShow: true,
arrowSize: 5, arrowSize: 5,
position: 'bottom', position: 'bottom',
style: 'default', style: 'default',
@@ -80,7 +87,7 @@ pluginOptions = {
showDuration: 400, showDuration: 400,
hideAnimation: 'slideUp', hideAnimation: 'slideUp',
hideDuration: 200, hideDuration: 200,
offsetY: 2, offsetY: 0,
offsetX: 0 offsetX: 0
}; };
@@ -108,11 +115,8 @@ getAnchorElement = function(element) {
return element; return element;
}; };
incr = function(obj, pos, val, useOpposite) { incr = function(obj, pos, val) {
var opp, temp; var opp, temp;
if (useOpposite == null) {
useOpposite = true;
}
if (typeof val === 'string') { if (typeof val === 'string') {
val = parseInt(val, 10); val = parseInt(val, 10);
} else if (typeof val !== 'number') { } else if (typeof val !== 'number') {
@@ -124,9 +128,6 @@ incr = function(obj, pos, val, useOpposite) {
opp = positions[opposites[pos.charAt(0)]]; opp = positions[opposites[pos.charAt(0)]];
temp = pos; temp = pos;
if (obj[opp] !== undefined) { if (obj[opp] !== undefined) {
if (!useOpposite) {
return;
}
pos = positions[opp.charAt(0)]; pos = positions[opp.charAt(0)];
val *= -1; val *= -1;
} }
@@ -135,10 +136,20 @@ incr = function(obj, pos, val, useOpposite) {
} else { } else {
obj[pos] += val; obj[pos] += val;
} }
console.log("incr (" + opp + ">>" + temp + ") " + pos + " by " + val);
return null; return null;
}; };
realign = function(alignment, inner, outer) {
if (alignment === 'l' || alignment === 't') {
return 0;
} else if (alignment === 'c' || alignment === 'm') {
return outer / 2 - inner / 2;
} else if (alignment === 'r' || alignment === 'b') {
return outer - inner;
}
throw "Invalid alignment";
};
insertCSS = function(style) { insertCSS = function(style) {
var elem; var elem;
if (!(style && style.css)) { if (!(style && style.css)) {
@@ -226,82 +237,88 @@ Notification = (function() {
} else if (!hidden && !show) { } else if (!hidden && !show) {
fn = this.options.hideAnimation; fn = this.options.hideAnimation;
args.push(this.options.hideDuration); args.push(this.options.hideDuration);
} else {
return callback();
} }
args.push(callback); args.push(callback);
return elems[fn].apply(elems, args); return elems[fn].apply(elems, args);
}; };
Notification.prototype.updatePosition = function() { Notification.prototype.updatePosition = function() {
var elementPosition, p, position, wrapperPosition; var arrowCss, color, contH, contW, css, elemH, elemPos, elemW, mainFull, margin, opp, oppFull, pAlign, pArrow, pMain, padding, pos, posFull, position, size, wrapPos, _i, _len;
if (!this.elem) { if (!this.elem) {
return; return;
} }
elementPosition = this.elem.position(); elemPos = this.elem.position();
wrapperPosition = this.wrapper.position(); elemH = this.elem.outerHeight();
elemW = this.elem.outerWidth();
wrapPos = this.wrapper.position();
contH = this.container.height();
contW = this.container.width();
position = this.getPosition(); position = this.getPosition();
console.log(this.elem[0]); console.log(position);
console.log("update position", position, " elem ", elementPosition, " main ", wrapperPosition); pMain = position[0];
p = {}; pAlign = position[1];
switch (position[0]) { pArrow = position[2] || pAlign;
case 'b': mainFull = positions[pMain];
incr(p, 'top', this.elem.outerHeight()); opp = opposites[pMain];
break; oppFull = positions[opp];
case 't': css = {};
incr(p, 'bottom', 0); css[oppFull] = pMain === 'b' ? elemH : pMain === 'r' ? elemW : 0;
break; incr(css, 'left', elemPos.left - wrapPos.left);
case 'l': margin = parseInt(this.elem.css("margin-left"), 10);
incr(p, 'right', 0); if (margin) {
break; incr(css, 'left', margin);
case 'r':
incr(p, 'left', this.elem.outerWidth());
break;
default:
throw "Unknown position: " + position;
} }
if (!navigator.userAgent.match(/MSIE/)) { if (/^inline/.test(this.elem.css('display'))) {
incr(p, 'top', elementPosition.top - wrapperPosition.top); padding = parseInt(this.elem.css("padding-top"), 10);
} if (padding) {
incr(p, 'left', elementPosition.left - wrapperPosition.left); incr(css, 'top', -padding);
return this.container.css(p);
};
Notification.prototype.updateArrow = function(p, position) {
var d, dir, size;
if (!(this.options.arrowShow && this.elementType !== 'radio')) {
this.arrow.hide();
return;
}
dir = arrowDirs[position];
size = this.options.arrowSize;
this.arrow.css('border-' + position, size + 'px solid ' + this.getColor());
this.arrow.css(p);
for (d in arrowDirs) {
if (d !== dir && d !== position) {
this.arrow.css('border-' + d, size + 'px solid transparent');
} }
} }
switch (position) { incr(css, 'top', this.options.offsetY);
case 'bottom': incr(css, 'left', this.options.offsetX);
p.top += size; if (!this.options.arrowShow) {
break; this.arrow.hide();
case 'right': } else {
p.left += size; size = this.options.arrowSize;
arrowCss = $.extend({}, css);
for (_i = 0, _len = mainPositions.length; _i < _len; _i++) {
pos = mainPositions[_i];
posFull = positions[pos];
if (pos === opp) {
continue;
}
color = posFull === mainFull ? this.getColor() : 'transparent';
arrowCss["border-" + posFull] = "" + size + "px solid " + color;
}
incr(css, positions[opp], size);
this.arrow.css(arrowCss).show();
} }
return this.arrow.show(); if (__indexOf.call(vPositions, pMain) >= 0) {
incr(css, 'left', realign(pAlign, contW, elemW));
}
if (__indexOf.call(hPositions, pMain) >= 0) {
incr(css, 'top', realign(pAlign, contH, elemH));
}
if (this.container.is(":visible")) {
css.display = 'block';
}
return this.container.removeAttr('style').css(css);
}; };
Notification.prototype.getPosition = function() { Notification.prototype.getPosition = function() {
var pos, text; var pos, text, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
text = this.options.position; text = this.options.position;
pos = parsePosition(text); pos = parsePosition(text);
if (pos.length === 0) { if (pos.length === 0) {
pos.push('b'); pos[0] = 'b';
} }
if (pos.length === 1 && pos[0] === 'l' || pos[0] === 'r') { if (_ref = pos[0], __indexOf.call(mainPositions, _ref) < 0) {
pos.push('m'); throw "Must be one of [" + mainPositions + "]";
} }
if (pos.length === 1) { if (pos.length === 1 || ((_ref1 = pos[0], __indexOf.call(vPositions, _ref1) >= 0) && (_ref2 = pos[1], __indexOf.call(hPositions, _ref2) < 0)) || ((_ref3 = pos[0], __indexOf.call(hPositions, _ref3) >= 0) && (_ref4 = pos[1], __indexOf.call(vPositions, _ref4) < 0))) {
pos.push('l'); pos[1] = (_ref5 = pos[0], __indexOf.call(hPositions, _ref5) >= 0) ? 'm' : 'l';
} }
if (!this.options.autoReposition) { if (!this.options.autoReposition) {
return pos; return pos;
@@ -338,8 +355,7 @@ Notification = (function() {
}; };
Notification.prototype.run = function(data, options) { Notification.prototype.run = function(data, options) {
var autohideTimer, var _this = this;
_this = this;
if ($.isPlainObject(options)) { if ($.isPlainObject(options)) {
$.extend(this.options, options); $.extend(this.options, options);
} else if ($.type(options) === 'string') { } else if ($.type(options) === 'string') {
@@ -360,7 +376,7 @@ Notification = (function() {
this.show(true); this.show(true);
if (this.options.autoHide) { if (this.options.autoHide) {
clearTimeout(this.autohideTimer); clearTimeout(this.autohideTimer);
return autohideTimer = setTimeout(function() { return this.autohideTimer = setTimeout(function() {
return _this.show(false); return _this.show(false);
}, this.options.autoHideDelay); }, this.options.autoHideDelay);
} }
+2 -2
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -10,9 +10,9 @@
margin: 150px; margin: 150px;
} }
.box { .box {
padding: 10px;
margin: 10px;
border: thin solid blue; border: thin solid blue;
padding: 10px;
margin: 20px;
width: 100px; width: 100px;
} }
.wrapper { .wrapper {
@@ -33,7 +33,7 @@
boxes.append($("<div class=\"wrapper\"/>").append(box)); boxes.append($("<div class=\"wrapper\"/>").append(box));
setTimeout(function() { setTimeout(function() {
box.notify(p, {position: p}); box.notify(p, {position: p+" middle"});
}, 500) }, 500)
}); });
</script> </script>
+92 -73
View File
@@ -13,6 +13,9 @@ positions =
l: 'left' l: 'left'
c: 'center' c: 'center'
r: 'right' r: 'right'
hPositions = ['l','c','r']
vPositions = ['t','m','b']
mainPositions = ['t','b','l','r']
#positions mapped to opposites #positions mapped to opposites
opposites = opposites =
t: 'b' t: 'b'
@@ -34,6 +37,7 @@ styles =
core: core:
html: """ html: """
<div class="#{className}Wrapper"> <div class="#{className}Wrapper">
<div class="#{className}Debug"></div>
<div class="#{className}Arrow"></div> <div class="#{className}Arrow"></div>
<div class="#{className}Container"></div> <div class="#{className}Container"></div>
</div> </div>
@@ -61,7 +65,13 @@ styles =
display: inline-block; display: inline-block;
height: 0; height: 0;
width: 0; width: 0;
border: thin solid red; }
.#{className}Debug {
position: absolute;
border: 3px solid red;
height: 0;
width: 0;
} }
.#{className}Container { .#{className}Container {
@@ -76,10 +86,8 @@ styles =
} }
.#{className}Arrow { .#{className}Arrow {
margin-top: 2px;
position: absolute; position: absolute;
z-index: 2; z-index: 2;
margin-left: 10px;
width: 0; width: 0;
height: 0; height: 0;
} }
@@ -136,7 +144,7 @@ styles =
pluginOptions = pluginOptions =
autoHide: false autoHide: false
autoHideDelay: 2000 autoHideDelay: 2000
arrowShow: false arrowShow: true
arrowSize: 5 arrowSize: 5
position: 'bottom' position: 'bottom'
# Default style # Default style
@@ -154,7 +162,7 @@ pluginOptions =
hideAnimation: 'slideUp' hideAnimation: 'slideUp'
hideDuration: 200 hideDuration: 200
# Gap between main and element # Gap between main and element
offsetY: 2 offsetY: 0
offsetX: 0 offsetX: 0
#TODO add z-index watches #TODO add z-index watches
#parents: { '.ui-dialog': 5001 } #parents: { '.ui-dialog': 5001 }
@@ -180,10 +188,8 @@ getAnchorElement = (element) ->
#custom-styled inputs - find thier real element #custom-styled inputs - find thier real element
element element
incr = (obj, pos, val, useOpposite = true) -> incr = (obj, pos, val) ->
# console.log "incr ---- #{pos} #{val} (#{typeof val})" # console.log "incr ---- #{pos} #{val} (#{typeof val})"
if typeof val is 'string' if typeof val is 'string'
val = parseInt val, 10 val = parseInt val, 10
else if typeof val isnt 'number' else if typeof val isnt 'number'
@@ -196,7 +202,6 @@ incr = (obj, pos, val, useOpposite = true) ->
#use the opposite if exists #use the opposite if exists
if obj[opp] isnt `undefined` if obj[opp] isnt `undefined`
return unless useOpposite
pos = positions[opp.charAt(0)] pos = positions[opp.charAt(0)]
val *= -1 val *= -1
@@ -204,10 +209,17 @@ incr = (obj, pos, val, useOpposite = true) ->
obj[pos] = val obj[pos] = val
else else
obj[pos] += val obj[pos] += val
console.log "incr (#{opp}>>#{temp}) #{pos} by #{val}"
null null
realign = (alignment, inner, outer) ->
return if alignment in ['l','t']
0
else if alignment in ['c','m']
outer/2 - inner/2
else if alignment in ['r','b']
outer - inner
throw "Invalid alignment"
insertCSS = (style) -> insertCSS = (style) ->
return unless style and style.css return unless style and style.css
elem = style.cssElem elem = style.cssElem
@@ -287,6 +299,8 @@ class Notification
else if not hidden and not show else if not hidden and not show
fn = @options.hideAnimation fn = @options.hideAnimation
args.push @options.hideDuration args.push @options.hideDuration
else
return callback()
args.push callback args.push callback
@@ -294,83 +308,88 @@ class Notification
updatePosition: -> updatePosition: ->
return unless @elem return unless @elem
#
elementPosition = @elem.position()
wrapperPosition = @wrapper.position()
# #grab some dimensions
elemPos = @elem.position()
elemH = @elem.outerHeight()
elemW = @elem.outerWidth()
wrapPos = @wrapper.position()
contH = @container.height()
contW = @container.width()
#get user defined position
position = @getPosition() position = @getPosition()
console.log position
console.log @elem[0] pMain = position[0]
console.log "update position", position, " elem ", elementPosition, " main ", wrapperPosition pAlign = position[1]
pArrow = position[2] or pAlign
#start calculations #start calculations
p = {} mainFull = positions[pMain]
switch position[0] opp = opposites[pMain]
when 'b' oppFull = positions[opp]
incr p, 'top', @elem.outerHeight() #initial positioning
when 't' css = {}
incr p, 'bottom', 0 css[oppFull] = if pMain is 'b' then elemH else
when 'l' if pMain is 'r' then elemW else 0
incr p, 'right', 0
when 'r'
incr p, 'left', @elem.outerWidth()
else
throw "Unknown position: #{position}"
#elem vs wrapper corrections #correct for elem-wrapper offset
unless navigator.userAgent.match /MSIE/ # unless navigator.userAgent.match /MSIE/
incr p, 'top', (elementPosition.top - wrapperPosition.top) # incr css, 'top', (elemPos.top - wrapPos.top)
incr css, 'left', elemPos.left - wrapPos.left
incr p, 'left', elementPosition.left - wrapperPosition.left #correct for left margin
margin = parseInt @elem.css("margin-left"), 10
# console.log "corrent margin" incr css, 'left', margin if margin
# for pos in ['top','right','bottom','left'] #correct for inline top padding
# incr p, pos, @elem.css("margin-#{pos}"), false if /^inline/.test @elem.css('display')
padding = parseInt @elem.css("padding-top"), 10
incr css, 'top', -padding if padding
#user defined offset
incr css, 'top', @options.offsetY
incr css, 'left', @options.offsetX
#set user offset #calculate arrow
# p.top += @options.offsetY if not @options.arrowShow
# p.left += @options.offsetX
# @updateArrow p, position
# if @setCSS
# @container.stop().animate(p)
# else
@container.css p
# @setCSS = true
updateArrow: (p, position) ->
unless @options.arrowShow and @elementType isnt 'radio'
@arrow.hide() @arrow.hide()
return else
size = @options.arrowSize
arrowCss = $.extend {}, css
#build arrow
for pos in mainPositions
posFull = positions[pos]
continue if pos is opp
color = if posFull is mainFull then @getColor() else 'transparent'
arrowCss["border-#{posFull}"] = "#{size}px solid #{color}"
#add some room for the arrow
incr css, positions[opp], size
#add styles
@arrow.css(arrowCss).show()
dir = arrowDirs[position] #calculate alignment
size = @options.arrowSize if pMain in vPositions
incr css, 'left', realign(pAlign, contW, elemW)
if pMain in hPositions
incr css, 'top', realign(pAlign, contH, elemH)
@arrow.css 'border-' + position, size + 'px solid ' + @getColor() #apply css
@arrow.css p css.display = 'block' if @container.is(":visible")
for d of arrowDirs @container.removeAttr('style').css css
if d isnt dir and d isnt position
@arrow.css 'border-' + d, size + 'px solid transparent'
switch position
when 'bottom'
p.top += size
when 'right'
p.left += size
@arrow.show()
getPosition: -> getPosition: ->
text = @options.position text = @options.position
pos = parsePosition text pos = parsePosition text
#if unspecified - choose bottom left #if unspecified - choose bottom left
pos.push 'b' if pos.length is 0 pos[0] = 'b' if pos.length is 0
pos.push 'm' if pos.length is 1 and pos[0] is 'l' or pos[0] is 'r'
pos.push 'l' if pos.length is 1 if pos[0] not in mainPositions
throw "Must be one of [#{mainPositions}]"
if pos.length is 1 or
(pos[0] in vPositions and pos[1] not in hPositions) or
(pos[0] in hPositions and pos[1] not in vPositions)
pos[1] = if pos[0] in hPositions then 'm' else 'l'
unless @options.autoReposition unless @options.autoReposition
return pos return pos
@@ -431,7 +450,7 @@ class Notification
#autohide #autohide
if @options.autoHide if @options.autoHide
clearTimeout @autohideTimer clearTimeout @autohideTimer
autohideTimer = setTimeout => @autohideTimer = setTimeout =>
@show false @show false
, @options.autoHideDelay , @options.autoHideDelay