Permalink
Browse files

and thusly, Adjacent List was brought to this world

  • Loading branch information...
0 parents commit 27f0d9ae0f8f7c35b783902250fa118b2fcf1490 @mislav committed May 4, 2008
Showing with 4,578 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +42 −0 adjacent-list.css
  3. +181 −0 adjacent-list.js
  4. +4,221 −0 prototype.js
  5. +133 −0 sample/index.html
@@ -0,0 +1 @@
+*.js
@@ -0,0 +1,42 @@
+/*html { background:silver; height:100%; overflow:auto; }*/
+body { margin:0; padding:1em 2em; font:small Verdana, sans-serif; background:white; }
+h1 { color:crimson; }
+h2 { color: #444; font-size: 1.3em; font-weight: normal }
+
+.adjacent-list { background:#ddd; border:1px solid #aaa; width: 80% }
+.adjacent-list { height:10.2em; }
+.adjacent-list:after{content:".";display:block;height:0;clear:both;visibility:hidden;}
+
+.adjacent-list div.wrapper, .adjacent-list ol { height:100%; }
+.adjacent-list div.wrapper { width:200%; }
+.adjacent-list ol {
+ background:white;
+ list-style:none;
+ padding:0; margin:0;
+ float:left; overflow:auto;
+ width: 12%;
+}
+.adjacent-list li { display:inline; white-space: nowrap }
+.adjacent-list span.parent { display:none; }
+.adjacent-list li.has-sublist a, .adjacent-list li.has-sublist span.class { background: #ffd; }
+.adjacent-list li.expand a, .adjacent-list li.expand span.class { background:gray; color:white; }
+.adjacent-list li.selected a, .adjacent-list li.selected span.class { background:crimson; color:white; }
+.adjacent-list a, .adjacent-list span.class {
+ display:block;
+ padding:0.2em 0.3em;
+ color:black;
+ text-decoration:none;
+ cursor:pointer;
+ position: relative;
+}
+.adjacent-list span.expand {
+ display: block; position: absolute; top:0; right: 0;
+ padding: 0 .2em;
+ font: bold 1.3em Verdana, sans-serif;
+ color: gray;
+}
+.adjacent-list li.expand span.expand { color: #444 }
+.adjacent-list li.selected span.expand { color: #eee }
+
+.adjacent-list span.class { color: #333; font-style: italic }
+.adjacent-list a:focus { outline:none; }
@@ -0,0 +1,181 @@
+var AdjacentList = Class.create({
+ initialize: function (el) {
+ el = $(el);
+
+ if ($w("ol ul").include(el.nodeName.toLowerCase())) {
+ var list = el
+ el = list.wrap().addClassName('adjacent-list')
+ } else {
+ var list = el.down('ol') || el.down('ul')
+ }
+
+ // stack of opened stuff
+ this.stack = [];
+ this.stack.push(list);
+ this.lastSelected = null;
+
+ this.position = 0; // track carousel
+ this.maxLevel = 0;
+
+ // wrap everything in a div that will act as carousel
+ this.wrapper = list.wrap().addClassName('wrapper').makePositioned().setStyle({top:0, left:0})
+ el.makeClipping()
+
+ this.effect = new AdjacentList.SetStyleEffect(this.wrapper, {duration: .2})
+
+ // traverse nested lists and make a mess out of it
+ this.listSetup(list);
+ var _this = this
+ this.wrapper.observe('click', function(e) {
+ var li = e.findElement('li')
+ if (li) _this.itemToggle(li)
+ }).observe('mouseover', function(e) {
+ if (e.element().match('span.expand')) _this.itemToggle(e.element().up('li'), true)
+ })
+
+ this.columnWidth = Number(list.getWidth())
+ },
+
+ listSetup: function (list, level) {
+ level = level || 1;
+ if (level > this.maxLevel) this.maxLevel = level
+
+ list.immediateDescendants().each(function(li){
+ li.level = level;
+
+ if (li.sublist = li.down('ol') || li.down('ul')) {
+ // extract sublist from nesting to make adjacent list possible
+ this.wrapper.appendChild(li.sublist);
+ li.sublist.hide()
+ li.addClassName('has-sublist');
+
+ this.listSetup(li.sublist, level + 1);
+
+ var item = li.down('a') || li.down('span.class')
+ if (item) item.insert(' <span class="expand">&raquo;</span>')
+ }
+ }, this);
+ },
+
+ itemToggle: function (item, noActivate) {
+ if (item.hasClassName('expand')) return false
+
+ while (this.stack.length > item.level) {
+ var prev = this.stack.pop();
+ prev.removeClassName('expand');
+ if (prev.sublist) prev.sublist.hide();
+ }
+ if (!noActivate) {
+ if (this.lastSelected) this.lastSelected.removeClassName('selected');
+ item.addClassName('selected');
+ this.lastSelected = item;
+ }
+
+ var shifting = this.checkCarousel(item.level + (item.sublist ? 1 : 0))
+
+ if (item.sublist) {
+ item.addClassName('expand');
+
+ if (this.effect && !shifting) {
+ var fx = new AdjacentList.SetStyleEffect(item.sublist, {duration: .2})
+ item.sublist.setStyle({ opacity:0 }).show()
+ fx.start({ opacity: 1 })
+ }
+ else item.sublist.show()
+ }
+
+ this.stack.push(item);
+ return true
+ },
+
+ checkCarousel: function (targetLevel) {
+ if (targetLevel < 1 || targetLevel > this.maxLevel) {
+ console.warn('targetLevel (%d) out of bounds, ignoring ...', targetLevel)
+ }
+ else {
+ var delta, targetPosition = targetLevel - this.columns
+ if (targetPosition < 0) targetPosition = 0
+ delta = targetPosition - this.position
+
+ if (delta) {
+ // console.log("shift by: %d", delta)
+ this.position = targetPosition
+ var newIndent = -this.position * this.columnWidth
+ if (this.effect) this.effect.start({ left: newIndent })
+ else this.wrapper.style.left = newIndent
+
+ return true
+ }
+ }
+ return false
+ }
+})
+
+// based on Bernie's Animator
+AdjacentList.Effect = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend({
+ interval: 20,
+ duration: 0.4,
+ onComplete: function(){},
+ transition: function(pos){ return ((-Math.cos(pos*Math.PI)/2) + 0.5) }
+ }, options);
+ },
+ seekFromTo: function(from, to) {
+ this.target = Math.max(0, Math.min(1, to));
+ this.state = Math.max(0, Math.min(1, from));
+ if (!this.interval) {
+ var _this = this, ticker = function(){ _this.tick() }
+ this.interval = window.setInterval(ticker, this.options.interval);
+ }
+ },
+ start: function() {
+ this.seekFromTo(0, 1)
+ },
+ tick: function() {
+ var movement = (this.options.interval / (this.options.duration * 1000)) * (this.state < this.target ? 1 : -1);
+ if (Math.abs(movement) >= Math.abs(this.state - this.target)) {
+ this.state = this.target;
+ } else {
+ this.state += movement;
+ }
+
+ try {
+ this.transition(this.options.transition(this.state));
+ } finally {
+ if (this.target == this.state) {
+ window.clearInterval(this.interval);
+ this.interval = null;
+ this.options.onComplete.call(this);
+ }
+ }
+ },
+ transition: function(value) {
+ console.log(value)
+ }
+})
+
+AdjacentList.SetStyleEffect = Class.create(AdjacentList.Effect, {
+ initialize: function($super, element, options) {
+ $super(options)
+ this.element = $(element)
+ },
+ transition: function(value) {
+ var style = {}
+ this.properties.each(function(pair) {
+ var current = (pair.value[1] - pair.value[0]) * value + pair.value[0]
+ style[pair.key] = current
+ if (pair.key != 'opacity') style[pair.key] += 'px'
+ })
+ this.element.setStyle(style)
+ },
+ start: function($super, properties) {
+ this.properties = new Hash
+ $H(properties).each(function(pair) {
+ var from = this.element.getStyle(pair.key)
+ if (Object.isString(from)) from = Number(from.replace(/px|pt/, ''))
+ this.properties.set(pair.key, [from, pair.value])
+ }, this)
+ $super()
+ }
+})
Oops, something went wrong.

0 comments on commit 27f0d9a

Please sign in to comment.