Visual.js 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735
  1. /***
  2. MochiKit.Visual 1.4
  3. See <http://mochikit.com/> for documentation, downloads, license, etc.
  4. (c) 2005 Bob Ippolito and others. All rights Reserved.
  5. ***/
  6. if (typeof(dojo) != 'undefined') {
  7. dojo.provide('MochiKit.Visual');
  8. dojo.require('MochiKit.Base');
  9. dojo.require('MochiKit.DOM');
  10. dojo.require('MochiKit.Style');
  11. dojo.require('MochiKit.Color');
  12. dojo.require('MochiKit.Iter');
  13. }
  14. if (typeof(JSAN) != 'undefined') {
  15. JSAN.use("MochiKit.Base", []);
  16. JSAN.use("MochiKit.DOM", []);
  17. JSAN.use("MochiKit.Style", []);
  18. JSAN.use("MochiKit.Color", []);
  19. JSAN.use("MochiKit.Iter", []);
  20. }
  21. try {
  22. if (typeof(MochiKit.Base) === 'undefined' ||
  23. typeof(MochiKit.DOM) === 'undefined' ||
  24. typeof(MochiKit.Style) === 'undefined' ||
  25. typeof(MochiKit.Color) === 'undefined' ||
  26. typeof(MochiKit.Iter) === 'undefined') {
  27. throw "";
  28. }
  29. } catch (e) {
  30. throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM, MochiKit.Style, MochiKit.Color and MochiKit.Iter!";
  31. }
  32. if (typeof(MochiKit.Visual) == "undefined") {
  33. MochiKit.Visual = {};
  34. }
  35. MochiKit.Visual.NAME = "MochiKit.Visual";
  36. MochiKit.Visual.VERSION = "1.4";
  37. MochiKit.Visual.__repr__ = function () {
  38. return "[" + this.NAME + " " + this.VERSION + "]";
  39. };
  40. MochiKit.Visual.toString = function () {
  41. return this.__repr__();
  42. };
  43. MochiKit.Visual._RoundCorners = function (e, options) {
  44. e = MochiKit.DOM.getElement(e);
  45. this._setOptions(options);
  46. if (this.options.__unstable__wrapElement) {
  47. e = this._doWrap(e);
  48. }
  49. var color = this.options.color;
  50. var C = MochiKit.Color.Color;
  51. if (this.options.color === "fromElement") {
  52. color = C.fromBackground(e);
  53. } else if (!(color instanceof C)) {
  54. color = C.fromString(color);
  55. }
  56. this.isTransparent = (color.asRGB().a <= 0);
  57. var bgColor = this.options.bgColor;
  58. if (this.options.bgColor === "fromParent") {
  59. bgColor = C.fromBackground(e.offsetParent);
  60. } else if (!(bgColor instanceof C)) {
  61. bgColor = C.fromString(bgColor);
  62. }
  63. this._roundCornersImpl(e, color, bgColor);
  64. };
  65. MochiKit.Visual._RoundCorners.prototype = {
  66. _doWrap: function (e) {
  67. var parent = e.parentNode;
  68. var doc = MochiKit.DOM.currentDocument();
  69. if (typeof(doc.defaultView) === "undefined"
  70. || doc.defaultView === null) {
  71. return e;
  72. }
  73. var style = doc.defaultView.getComputedStyle(e, null);
  74. if (typeof(style) === "undefined" || style === null) {
  75. return e;
  76. }
  77. var wrapper = MochiKit.DOM.DIV({"style": {
  78. display: "block",
  79. // convert padding to margin
  80. marginTop: style.getPropertyValue("padding-top"),
  81. marginRight: style.getPropertyValue("padding-right"),
  82. marginBottom: style.getPropertyValue("padding-bottom"),
  83. marginLeft: style.getPropertyValue("padding-left"),
  84. // remove padding so the rounding looks right
  85. padding: "0px"
  86. /*
  87. paddingRight: "0px",
  88. paddingLeft: "0px"
  89. */
  90. }});
  91. wrapper.innerHTML = e.innerHTML;
  92. e.innerHTML = "";
  93. e.appendChild(wrapper);
  94. return e;
  95. },
  96. _roundCornersImpl: function (e, color, bgColor) {
  97. if (this.options.border) {
  98. this._renderBorder(e, bgColor);
  99. }
  100. if (this._isTopRounded()) {
  101. this._roundTopCorners(e, color, bgColor);
  102. }
  103. if (this._isBottomRounded()) {
  104. this._roundBottomCorners(e, color, bgColor);
  105. }
  106. },
  107. _renderBorder: function (el, bgColor) {
  108. var borderValue = "1px solid " + this._borderColor(bgColor);
  109. var borderL = "border-left: " + borderValue;
  110. var borderR = "border-right: " + borderValue;
  111. var style = "style='" + borderL + ";" + borderR + "'";
  112. el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
  113. },
  114. _roundTopCorners: function (el, color, bgColor) {
  115. var corner = this._createCorner(bgColor);
  116. for (var i = 0; i < this.options.numSlices; i++) {
  117. corner.appendChild(
  118. this._createCornerSlice(color, bgColor, i, "top")
  119. );
  120. }
  121. el.style.paddingTop = 0;
  122. el.insertBefore(corner, el.firstChild);
  123. },
  124. _roundBottomCorners: function (el, color, bgColor) {
  125. var corner = this._createCorner(bgColor);
  126. for (var i = (this.options.numSlices - 1); i >= 0; i--) {
  127. corner.appendChild(
  128. this._createCornerSlice(color, bgColor, i, "bottom")
  129. );
  130. }
  131. el.style.paddingBottom = 0;
  132. el.appendChild(corner);
  133. },
  134. _createCorner: function (bgColor) {
  135. var dom = MochiKit.DOM;
  136. return dom.DIV({style: {backgroundColor: bgColor.toString()}});
  137. },
  138. _createCornerSlice: function (color, bgColor, n, position) {
  139. var slice = MochiKit.DOM.SPAN();
  140. var inStyle = slice.style;
  141. inStyle.backgroundColor = color.toString();
  142. inStyle.display = "block";
  143. inStyle.height = "1px";
  144. inStyle.overflow = "hidden";
  145. inStyle.fontSize = "1px";
  146. var borderColor = this._borderColor(color, bgColor);
  147. if (this.options.border && n === 0) {
  148. inStyle.borderTopStyle = "solid";
  149. inStyle.borderTopWidth = "1px";
  150. inStyle.borderLeftWidth = "0px";
  151. inStyle.borderRightWidth = "0px";
  152. inStyle.borderBottomWidth = "0px";
  153. // assumes css compliant box model
  154. inStyle.height = "0px";
  155. inStyle.borderColor = borderColor.toString();
  156. } else if (borderColor) {
  157. inStyle.borderColor = borderColor.toString();
  158. inStyle.borderStyle = "solid";
  159. inStyle.borderWidth = "0px 1px";
  160. }
  161. if (!this.options.compact && (n == (this.options.numSlices - 1))) {
  162. inStyle.height = "2px";
  163. }
  164. this._setMargin(slice, n, position);
  165. this._setBorder(slice, n, position);
  166. return slice;
  167. },
  168. _setOptions: function (options) {
  169. this.options = {
  170. corners: "all",
  171. color: "fromElement",
  172. bgColor: "fromParent",
  173. blend: true,
  174. border: false,
  175. compact: false,
  176. __unstable__wrapElement: false
  177. };
  178. MochiKit.Base.update(this.options, options);
  179. this.options.numSlices = (this.options.compact ? 2 : 4);
  180. },
  181. _whichSideTop: function () {
  182. var corners = this.options.corners;
  183. if (this._hasString(corners, "all", "top")) {
  184. return "";
  185. }
  186. var has_tl = (corners.indexOf("tl") != -1);
  187. var has_tr = (corners.indexOf("tr") != -1);
  188. if (has_tl && has_tr) {
  189. return "";
  190. }
  191. if (has_tl) {
  192. return "left";
  193. }
  194. if (has_tr) {
  195. return "right";
  196. }
  197. return "";
  198. },
  199. _whichSideBottom: function () {
  200. var corners = this.options.corners;
  201. if (this._hasString(corners, "all", "bottom")) {
  202. return "";
  203. }
  204. var has_bl = (corners.indexOf('bl') != -1);
  205. var has_br = (corners.indexOf('br') != -1);
  206. if (has_bl && has_br) {
  207. return "";
  208. }
  209. if (has_bl) {
  210. return "left";
  211. }
  212. if (has_br) {
  213. return "right";
  214. }
  215. return "";
  216. },
  217. _borderColor: function (color, bgColor) {
  218. if (color == "transparent") {
  219. return bgColor;
  220. } else if (this.options.border) {
  221. return this.options.border;
  222. } else if (this.options.blend) {
  223. return bgColor.blendedColor(color);
  224. }
  225. return "";
  226. },
  227. _setMargin: function (el, n, corners) {
  228. var marginSize = this._marginSize(n) + "px";
  229. var whichSide = (
  230. corners == "top" ? this._whichSideTop() : this._whichSideBottom()
  231. );
  232. var style = el.style;
  233. if (whichSide == "left") {
  234. style.marginLeft = marginSize;
  235. style.marginRight = "0px";
  236. } else if (whichSide == "right") {
  237. style.marginRight = marginSize;
  238. style.marginLeft = "0px";
  239. } else {
  240. style.marginLeft = marginSize;
  241. style.marginRight = marginSize;
  242. }
  243. },
  244. _setBorder: function (el, n, corners) {
  245. var borderSize = this._borderSize(n) + "px";
  246. var whichSide = (
  247. corners == "top" ? this._whichSideTop() : this._whichSideBottom()
  248. );
  249. var style = el.style;
  250. if (whichSide == "left") {
  251. style.borderLeftWidth = borderSize;
  252. style.borderRightWidth = "0px";
  253. } else if (whichSide == "right") {
  254. style.borderRightWidth = borderSize;
  255. style.borderLeftWidth = "0px";
  256. } else {
  257. style.borderLeftWidth = borderSize;
  258. style.borderRightWidth = borderSize;
  259. }
  260. },
  261. _marginSize: function (n) {
  262. if (this.isTransparent) {
  263. return 0;
  264. }
  265. var o = this.options;
  266. if (o.compact && o.blend) {
  267. var smBlendedMarginSizes = [1, 0];
  268. return smBlendedMarginSizes[n];
  269. } else if (o.compact) {
  270. var compactMarginSizes = [2, 1];
  271. return compactMarginSizes[n];
  272. } else if (o.blend) {
  273. var blendedMarginSizes = [3, 2, 1, 0];
  274. return blendedMarginSizes[n];
  275. } else {
  276. var marginSizes = [5, 3, 2, 1];
  277. return marginSizes[n];
  278. }
  279. },
  280. _borderSize: function (n) {
  281. var o = this.options;
  282. var borderSizes;
  283. if (o.compact && (o.blend || this.isTransparent)) {
  284. return 1;
  285. } else if (o.compact) {
  286. borderSizes = [1, 0];
  287. } else if (o.blend) {
  288. borderSizes = [2, 1, 1, 1];
  289. } else if (o.border) {
  290. borderSizes = [0, 2, 0, 0];
  291. } else if (this.isTransparent) {
  292. borderSizes = [5, 3, 2, 1];
  293. } else {
  294. return 0;
  295. }
  296. return borderSizes[n];
  297. },
  298. _hasString: function (str) {
  299. for (var i = 1; i< arguments.length; i++) {
  300. if (str.indexOf(arguments[i]) != -1) {
  301. return true;
  302. }
  303. }
  304. return false;
  305. },
  306. _isTopRounded: function () {
  307. return this._hasString(this.options.corners,
  308. "all", "top", "tl", "tr"
  309. );
  310. },
  311. _isBottomRounded: function () {
  312. return this._hasString(this.options.corners,
  313. "all", "bottom", "bl", "br"
  314. );
  315. },
  316. _hasSingleTextChild: function (el) {
  317. return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3);
  318. }
  319. };
  320. MochiKit.Visual.roundElement = function (e, options) {
  321. new MochiKit.Visual._RoundCorners(e, options);
  322. };
  323. MochiKit.Visual.roundClass = function (tagName, className, options) {
  324. var elements = MochiKit.DOM.getElementsByTagAndClassName(
  325. tagName, className
  326. );
  327. for (var i = 0; i < elements.length; i++) {
  328. MochiKit.Visual.roundElement(elements[i], options);
  329. }
  330. };
  331. MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) {
  332. /***
  333. Change a node text to character in tags.
  334. @param tagifyStyle: the style to apply to character nodes, default to
  335. 'position: relative'.
  336. ***/
  337. var tagifyStyle = tagifyStyle || 'position:relative';
  338. if (MochiKit.Base.isIE()) {
  339. tagifyStyle += ';zoom:1';
  340. }
  341. element = MochiKit.DOM.getElement(element);
  342. var fe = MochiKit.Iter.forEach;
  343. fe(element.childNodes, function (child) {
  344. if (child.nodeType == 3) {
  345. fe(child.nodeValue.split(''), function (character) {
  346. element.insertBefore(
  347. MochiKit.DOM.SPAN({style: tagifyStyle},
  348. character == ' ' ? String.fromCharCode(160) : character), child);
  349. });
  350. MochiKit.DOM.removeElement(child);
  351. }
  352. });
  353. };
  354. MochiKit.Visual.forceRerendering = function (element) {
  355. try {
  356. element = MochiKit.DOM.getElement(element);
  357. var n = document.createTextNode(' ');
  358. element.appendChild(n);
  359. element.removeChild(n);
  360. } catch(e) {
  361. }
  362. };
  363. MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
  364. /***
  365. Launch the same effect subsequently on given elements.
  366. ***/
  367. options = MochiKit.Base.update({
  368. speed: 0.1, delay: 0.0
  369. }, options || {});
  370. var masterDelay = options.delay;
  371. var index = 0;
  372. MochiKit.Iter.forEach(elements, function (innerelement) {
  373. options.delay = index * options.speed + masterDelay;
  374. new effect(innerelement, options);
  375. index += 1;
  376. });
  377. };
  378. MochiKit.Visual.PAIRS = {
  379. 'slide': ['slideDown', 'slideUp'],
  380. 'blind': ['blindDown', 'blindUp'],
  381. 'appear': ['appear', 'fade'],
  382. 'size': ['grow', 'shrink']
  383. };
  384. MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) {
  385. /***
  386. Toggle an item between two state depending of its visibility, making
  387. a effect between these states. Default effect is 'appear', can be
  388. 'slide' or 'blind'.
  389. ***/
  390. element = MochiKit.DOM.getElement(element);
  391. effect = (effect || 'appear').toLowerCase();
  392. options = MochiKit.Base.update({
  393. queue: {position: 'end', scope: (element.id || 'global'), limit: 1}
  394. }, options || {});
  395. var v = MochiKit.Visual;
  396. v[MochiKit.DOM.isVisible(element) ?
  397. v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options);
  398. };
  399. /***
  400. Transitions: define functions calculating variations depending of a position.
  401. ***/
  402. MochiKit.Visual.Transitions = {}
  403. MochiKit.Visual.Transitions.linear = function (pos) {
  404. return pos;
  405. };
  406. MochiKit.Visual.Transitions.sinoidal = function (pos) {
  407. return (-Math.cos(pos*Math.PI)/2) + 0.5;
  408. };
  409. MochiKit.Visual.Transitions.reverse = function (pos) {
  410. return 1 - pos;
  411. };
  412. MochiKit.Visual.Transitions.flicker = function (pos) {
  413. return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
  414. };
  415. MochiKit.Visual.Transitions.wobble = function (pos) {
  416. return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
  417. };
  418. MochiKit.Visual.Transitions.pulse = function (pos) {
  419. return (Math.floor(pos*10) % 2 == 0 ?
  420. (pos*10 - Math.floor(pos*10)) : 1 - (pos*10 - Math.floor(pos*10)));
  421. };
  422. MochiKit.Visual.Transitions.none = function (pos) {
  423. return 0;
  424. };
  425. MochiKit.Visual.Transitions.full = function (pos) {
  426. return 1;
  427. };
  428. /***
  429. Core effects
  430. ***/
  431. MochiKit.Visual.ScopedQueue = function () {
  432. this.__init__();
  433. };
  434. MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
  435. __init__: function () {
  436. this.effects = [];
  437. this.interval = null;
  438. },
  439. add: function (effect) {
  440. var timestamp = new Date().getTime();
  441. var position = (typeof(effect.options.queue) == 'string') ?
  442. effect.options.queue : effect.options.queue.position;
  443. var fe = MochiKit.Iter.forEach;
  444. switch (position) {
  445. case 'front':
  446. // move unstarted effects after this effect
  447. fe(this.effects, function (e) {
  448. if (e.state == 'idle') {
  449. e.startOn += effect.finishOn;
  450. e.finishOn += effect.finishOn;
  451. }
  452. });
  453. break;
  454. case 'end':
  455. var finish;
  456. // start effect after last queued effect has finished
  457. fe(this.effects, function (e) {
  458. var i = e.finishOn;
  459. if (i >= (finish || i)) {
  460. finish = i;
  461. }
  462. });
  463. timestamp = finish || timestamp;
  464. break;
  465. }
  466. effect.startOn += timestamp;
  467. effect.finishOn += timestamp;
  468. if (!effect.options.queue.limit ||
  469. this.effects.length < effect.options.queue.limit) {
  470. this.effects.push(effect);
  471. }
  472. if (!this.interval) {
  473. this.interval = setInterval(MochiKit.Base.bind(this.loop, this),
  474. 40);
  475. }
  476. },
  477. remove: function (effect) {
  478. this.effects = MochiKit.Base.filter(function (e) {
  479. return e != effect;
  480. }, this.effects);
  481. if (this.effects.length == 0) {
  482. clearInterval(this.interval);
  483. this.interval = null;
  484. }
  485. },
  486. loop: function () {
  487. var timePos = new Date().getTime();
  488. MochiKit.Iter.forEach(this.effects, function (effect) {
  489. effect.loop(timePos);
  490. });
  491. }
  492. });
  493. MochiKit.Visual.Queues = {
  494. instances: {},
  495. get: function (queueName) {
  496. if (typeof(queueName) != 'string') {
  497. return queueName;
  498. }
  499. if (!this.instances[queueName]) {
  500. this.instances[queueName] = new MochiKit.Visual.ScopedQueue();
  501. }
  502. return this.instances[queueName];
  503. }
  504. };
  505. MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global');
  506. MochiKit.Visual.DefaultOptions = {
  507. transition: MochiKit.Visual.Transitions.sinoidal,
  508. duration: 1.0, // seconds
  509. fps: 25.0, // max. 25fps due to MochiKit.Visual.Queue implementation
  510. sync: false, // true for combining
  511. from: 0.0,
  512. to: 1.0,
  513. delay: 0.0,
  514. queue: 'parallel'
  515. };
  516. MochiKit.Visual.Base = function () {};
  517. MochiKit.Visual.Base.prototype = {
  518. /***
  519. Basic class for all Effects. Define a looping mechanism called for each step
  520. of an effect. Don't instantiate it, only subclass it.
  521. ***/
  522. __class__ : MochiKit.Visual.Base,
  523. start: function (options) {
  524. var v = MochiKit.Visual;
  525. this.options = MochiKit.Base.setdefault(options || {},
  526. v.DefaultOptions);
  527. this.currentFrame = 0;
  528. this.state = 'idle';
  529. this.startOn = this.options.delay*1000;
  530. this.finishOn = this.startOn + (this.options.duration*1000);
  531. this.event('beforeStart');
  532. if (!this.options.sync) {
  533. v.Queues.get(typeof(this.options.queue) == 'string' ?
  534. 'global' : this.options.queue.scope).add(this);
  535. }
  536. },
  537. loop: function (timePos) {
  538. if (timePos >= this.startOn) {
  539. if (timePos >= this.finishOn) {
  540. this.render(1.0);
  541. this.cancel();
  542. this.event('beforeFinish');
  543. this.finish();
  544. this.event('afterFinish');
  545. return;
  546. }
  547. var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
  548. var frame =
  549. Math.round(pos * this.options.fps * this.options.duration);
  550. if (frame > this.currentFrame) {
  551. this.render(pos);
  552. this.currentFrame = frame;
  553. }
  554. }
  555. },
  556. render: function (pos) {
  557. if (this.state == 'idle') {
  558. this.state = 'running';
  559. this.event('beforeSetup');
  560. this.setup();
  561. this.event('afterSetup');
  562. }
  563. if (this.state == 'running') {
  564. if (this.options.transition) {
  565. pos = this.options.transition(pos);
  566. }
  567. pos *= (this.options.to - this.options.from);
  568. pos += this.options.from;
  569. this.event('beforeUpdate');
  570. this.update(pos);
  571. this.event('afterUpdate');
  572. }
  573. },
  574. cancel: function () {
  575. if (!this.options.sync) {
  576. MochiKit.Visual.Queues.get(typeof(this.options.queue) == 'string' ?
  577. 'global' : this.options.queue.scope).remove(this);
  578. }
  579. this.state = 'finished';
  580. },
  581. setup: function () {
  582. },
  583. finish: function () {
  584. },
  585. update: function (position) {
  586. },
  587. event: function (eventName) {
  588. if (this.options[eventName + 'Internal']) {
  589. this.options[eventName + 'Internal'](this);
  590. }
  591. if (this.options[eventName]) {
  592. this.options[eventName](this);
  593. }
  594. },
  595. repr: function () {
  596. return '[' + this.__class__.NAME + ', options:' +
  597. MochiKit.Base.repr(this.options) + ']';
  598. }
  599. }
  600. MochiKit.Visual.Parallel = function (effects, options) {
  601. this.__init__(effects, options);
  602. };
  603. MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base();
  604. MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, {
  605. /***
  606. Run multiple effects at the same time.
  607. ***/
  608. __init__: function (effects, options) {
  609. this.effects = effects || [];
  610. this.start(options);
  611. },
  612. update: function (position) {
  613. MochiKit.Iter.forEach(this.effects, function (effect) {
  614. effect.render(position);
  615. });
  616. },
  617. finish: function () {
  618. MochiKit.Iter.forEach(this.effects, function (effect) {
  619. effect.render(1.0);
  620. effect.cancel();
  621. effect.event('beforeFinish');
  622. effect.finish();
  623. effect.event('afterFinish');
  624. });
  625. }
  626. });
  627. MochiKit.Visual.Opacity = function (element, options) {
  628. this.__init__(element, options);
  629. };
  630. MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base();
  631. MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, {
  632. /***
  633. Change the opacity of an element.
  634. @param options: 'from' and 'to' change the starting and ending opacities.
  635. Must be between 0.0 and 1.0. Default to current opacity and 1.0.
  636. ***/
  637. __init__: function (element, /* optional */options) {
  638. var b = MochiKit.Base;
  639. var d = MochiKit.DOM;
  640. this.element = d.getElement(element);
  641. // make this work on IE on elements without 'layout'
  642. if (b.isIE() && (!this.element.currentStyle.hasLayout)) {
  643. d.setStyle(this.element, {zoom: 1});
  644. }
  645. options = b.update({
  646. from: d.getOpacity(this.element) || 0.0,
  647. to: 1.0
  648. }, options || {});
  649. this.start(options);
  650. },
  651. update: function (position) {
  652. MochiKit.DOM.setOpacity(this.element, position);
  653. }
  654. });
  655. MochiKit.Visual.Move = function (element, options) {
  656. this.__init__(element, options);
  657. };
  658. MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base();
  659. MochiKit.Base.update(MochiKit.Visual.Move.prototype, {
  660. /***
  661. Move an element between its current position to a defined position
  662. @param options: 'x' and 'y' for final positions, default to 0, 0.
  663. ***/
  664. __init__: function (element, /* optional */options) {
  665. this.element = MochiKit.DOM.getElement(element);
  666. options = MochiKit.Base.update({
  667. x: 0,
  668. y: 0,
  669. mode: 'relative'
  670. }, options || {});
  671. this.start(options);
  672. },
  673. setup: function () {
  674. var d = MochiKit.DOM;
  675. // Bug in Opera: Opera returns the 'real' position of a static element
  676. // or relative element that does not have top/left explicitly set.
  677. // ==> Always set top and left for position relative elements in your
  678. // stylesheets (to 0 if you do not need them)
  679. d.makePositioned(this.element);
  680. var s = this.element.style;
  681. var originalVisibility = s.visibility;
  682. var originalDisplay = s.display;
  683. if (originalDisplay == 'none') {
  684. s.visibility = 'hidden';
  685. s.display = '';
  686. }
  687. this.originalLeft = parseFloat(d.getStyle(this.element, 'left') || '0');
  688. this.originalTop = parseFloat(d.getStyle(this.element, 'top') || '0');
  689. if (this.options.mode == 'absolute') {
  690. // absolute movement, so we need to calc deltaX and deltaY
  691. this.options.x -= this.originalLeft;
  692. this.options.y -= this.originalTop;
  693. }
  694. if (originalDisplay == 'none') {
  695. s.visibility = originalVisibility;
  696. s.display = originalDisplay;
  697. }
  698. },
  699. update: function (position) {
  700. MochiKit.DOM.setStyle(this.element, {
  701. left: Math.round(this.options.x * position + this.originalLeft) + 'px',
  702. top: Math.round(this.options.y * position + this.originalTop) + 'px'
  703. });
  704. }
  705. });
  706. MochiKit.Visual.Scale = function (element, percent, options) {
  707. this.__init__(element, percent, options);
  708. };
  709. MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base();
  710. MochiKit.Base.update(MochiKit.Visual.Scale.prototype, {
  711. /***
  712. Change the size of an element.
  713. @param percent: final_size = percent*original_size
  714. @param options: several options changing scale behaviour
  715. ***/
  716. __init__: function (element, percent, /* optional */options) {
  717. this.element = MochiKit.DOM.getElement(element)
  718. options = MochiKit.Base.update({
  719. scaleX: true,
  720. scaleY: true,
  721. scaleContent: true,
  722. scaleFromCenter: false,
  723. scaleMode: 'box', // 'box' or 'contents' or {} with provided values
  724. scaleFrom: 100.0,
  725. scaleTo: percent
  726. }, options || {});
  727. this.start(options);
  728. },
  729. setup: function () {
  730. this.restoreAfterFinish = this.options.restoreAfterFinish || false;
  731. this.elementPositioning = MochiKit.DOM.getStyle(this.element,
  732. 'position');
  733. var fe = MochiKit.Iter.forEach;
  734. var b = MochiKit.Base.bind;
  735. this.originalStyle = {};
  736. fe(['top', 'left', 'width', 'height', 'fontSize'],
  737. b(function (k) {
  738. this.originalStyle[k] = this.element.style[k];
  739. }, this));
  740. this.originalTop = this.element.offsetTop;
  741. this.originalLeft = this.element.offsetLeft;
  742. var fontSize = MochiKit.DOM.getStyle(this.element,
  743. 'font-size') || '100%';
  744. fe(['em', 'px', '%'],
  745. b(function (fontSizeType) {
  746. if (fontSize.indexOf(fontSizeType) > 0) {
  747. this.fontSize = parseFloat(fontSize);
  748. this.fontSizeType = fontSizeType;
  749. }
  750. }, this));
  751. this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
  752. if (/^content/.test(this.options.scaleMode)) {
  753. this.dims = [this.element.scrollHeight, this.element.scrollWidth];
  754. } else if (this.options.scaleMode == 'box') {
  755. this.dims = [this.element.offsetHeight, this.element.offsetWidth];
  756. } else {
  757. this.dims = [this.options.scaleMode.originalHeight,
  758. this.options.scaleMode.originalWidth];
  759. }
  760. },
  761. update: function (position) {
  762. var currentScale = (this.options.scaleFrom/100.0) +
  763. (this.factor * position);
  764. if (this.options.scaleContent && this.fontSize) {
  765. MochiKit.DOM.setStyle(this.element, {
  766. fontSize: this.fontSize * currentScale + this.fontSizeType
  767. });
  768. }
  769. this.setDimensions(this.dims[0] * currentScale,
  770. this.dims[1] * currentScale);
  771. },
  772. finish: function () {
  773. if (this.restoreAfterFinish) {
  774. MochiKit.DOM.setStyle(this.element, this.originalStyle);
  775. }
  776. },
  777. setDimensions: function (height, width) {
  778. var d = {};
  779. if (this.options.scaleX) {
  780. d.width = Math.round(width) + 'px';
  781. }
  782. if (this.options.scaleY) {
  783. d.height = Math.round(height) + 'px';
  784. }
  785. if (this.options.scaleFromCenter) {
  786. var topd = (height - this.dims[0])/2;
  787. var leftd = (width - this.dims[1])/2;
  788. if (this.elementPositioning == 'absolute') {
  789. if (this.options.scaleY) {
  790. d.top = this.originalTop - topd + 'px';
  791. }
  792. if (this.options.scaleX) {
  793. d.left = this.originalLeft - leftd + 'px';
  794. }
  795. } else {
  796. if (this.options.scaleY) {
  797. d.top = -topd + 'px';
  798. }
  799. if (this.options.scaleX) {
  800. d.left = -leftd + 'px';
  801. }
  802. }
  803. }
  804. MochiKit.DOM.setStyle(this.element, d);
  805. }
  806. });
  807. MochiKit.Visual.Highlight = function (element, options) {
  808. this.__init__(element, options);
  809. };
  810. MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base();
  811. MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, {
  812. /***
  813. Highlight an item of the page.
  814. @param options: 'startcolor' for choosing highlighting color, default
  815. to '#ffff99'.
  816. ***/
  817. __init__: function (element, /* optional */options) {
  818. this.element = MochiKit.DOM.getElement(element);
  819. options = MochiKit.Base.update({
  820. startcolor: '#ffff99'
  821. }, options || {});
  822. this.start(options);
  823. },
  824. setup: function () {
  825. var d = MochiKit.DOM;
  826. var b = MochiKit.Base;
  827. // Prevent executing on elements not in the layout flow
  828. if (d.getStyle(this.element, 'display') == 'none') {
  829. this.cancel();
  830. return;
  831. }
  832. // Disable background image during the effect
  833. this.oldStyle = {
  834. backgroundImage: d.getStyle(this.element, 'background-image')
  835. };
  836. d.setStyle(this.element, {
  837. backgroundImage: 'none'
  838. });
  839. if (!this.options.endcolor) {
  840. this.options.endcolor =
  841. MochiKit.Color.Color.fromBackground(this.element).toHexString();
  842. }
  843. if (!this.options.restorecolor) {
  844. this.options.restorecolor = d.getStyle(this.element,
  845. 'background-color');
  846. }
  847. // init color calculations
  848. this._base = b.map(b.bind(function (i) {
  849. return parseInt(
  850. this.options.startcolor.slice(i*2 + 1, i*2 + 3), 16);
  851. }, this), [0, 1, 2]);
  852. this._delta = b.map(b.bind(function (i) {
  853. return parseInt(this.options.endcolor.slice(i*2 + 1, i*2 + 3), 16)
  854. - this._base[i];
  855. }, this), [0, 1, 2]);
  856. },
  857. update: function (position) {
  858. var m = '#';
  859. MochiKit.Iter.forEach([0, 1, 2], MochiKit.Base.bind(function (i) {
  860. m += MochiKit.Color.toColorPart(Math.round(this._base[i] +
  861. this._delta[i]*position));
  862. }, this));
  863. MochiKit.DOM.setStyle(this.element, {
  864. backgroundColor: m
  865. });
  866. },
  867. finish: function () {
  868. MochiKit.DOM.setStyle(this.element,
  869. MochiKit.Base.update(this.oldStyle, {
  870. backgroundColor: this.options.endcolor
  871. }));
  872. }
  873. });
  874. MochiKit.Visual.ScrollTo = function (element, options) {
  875. this.__init__(element, options);
  876. };
  877. MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base();
  878. MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, {
  879. /***
  880. Scroll to an element in the page.
  881. ***/
  882. __init__: function (element, /* optional */options) {
  883. this.element = MochiKit.DOM.getElement(element);
  884. this.start(options || {});
  885. },
  886. setup: function () {
  887. var p = MochiKit.Position;
  888. p.prepare();
  889. var offsets = p.cumulativeOffset(this.element);
  890. if (this.options.offset) {
  891. offsets.y += this.options.offset;
  892. }
  893. var max;
  894. if (window.innerHeight) {
  895. max = window.innerHeight - window.height;
  896. } else if (document.documentElement &&
  897. document.documentElement.clientHeight) {
  898. max = document.documentElement.clientHeight -
  899. document.body.scrollHeight;
  900. } else if (document.body) {
  901. max = document.body.clientHeight - document.body.scrollHeight;
  902. }
  903. this.scrollStart = p.windowOffset.y;
  904. this.delta = (offsets.y > max ? max : offsets.y) - this.scrollStart;
  905. },
  906. update: function (position) {
  907. var p = MochiKit.Position;
  908. p.prepare();
  909. window.scrollTo(p.windowOffset.x, this.scrollStart + (position * this.delta));
  910. }
  911. });
  912. /***
  913. Combination effects.
  914. ***/
  915. MochiKit.Visual.fade = function (element, /* optional */ options) {
  916. /***
  917. Fade a given element: change its opacity and hide it in the end.
  918. @param options: 'to' and 'from' to change opacity.
  919. ***/
  920. var d = MochiKit.DOM;
  921. var oldOpacity = d.getInlineOpacity(element);
  922. options = MochiKit.Base.update({
  923. from: d.getOpacity(element) || 1.0,
  924. to: 0.0,
  925. afterFinishInternal: function (effect) {
  926. if (effect.options.to !== 0) {
  927. return;
  928. }
  929. MochiKit.Style.hideElement(effect.element);
  930. d.setStyle(effect.element, {opacity: oldOpacity});
  931. }
  932. }, options || {});
  933. return new MochiKit.Visual.Opacity(element, options);
  934. };
  935. MochiKit.Visual.appear = function (element, /* optional */ options) {
  936. /***
  937. Make an element appear.
  938. @param options: 'to' and 'from' to change opacity.
  939. ***/
  940. var d = MochiKit.DOM;
  941. var v = MochiKit.Visual;
  942. options = MochiKit.Base.update({
  943. from: (d.getStyle(element, 'display') == 'none' ? 0.0 :
  944. d.getOpacity(element) || 0.0),
  945. to: 1.0,
  946. // force Safari to render floated elements properly
  947. afterFinishInternal: function (effect) {
  948. v.forceRerendering(effect.element);
  949. },
  950. beforeSetupInternal: function (effect) {
  951. d.setOpacity(effect.element, effect.options.from);
  952. MochiKit.Style.showElement(effect.element);
  953. }
  954. }, options || {});
  955. return new v.Opacity(element, options);
  956. };
  957. MochiKit.Visual.puff = function (element, /* optional */ options) {
  958. /***
  959. 'Puff' an element: grow it to double size, fading it and make it hidden.
  960. ***/
  961. var d = MochiKit.DOM;
  962. var v = MochiKit.Visual;
  963. element = d.getElement(element);
  964. var oldStyle = {
  965. opacity: d.getInlineOpacity(element),
  966. position: d.getStyle(element, 'position')
  967. };
  968. options = MochiKit.Base.update({
  969. beforeSetupInternal: function (effect) {
  970. d.setStyle(effect.effects[0].element,
  971. {position: 'absolute'});
  972. },
  973. afterFinishInternal: function (effect) {
  974. MochiKit.Style.hideElement(effect.effects[0].element);
  975. d.setStyle(effect.effects[0].element, oldStyle);
  976. }
  977. }, options || {});
  978. return new v.Parallel(
  979. [new v.Scale(element, 200,
  980. {sync: true, scaleFromCenter: true,
  981. scaleContent: true, restoreAfterFinish: true}),
  982. new v.Opacity(element, {sync: true, to: 0.0 })],
  983. options);
  984. };
  985. MochiKit.Visual.blindUp = function (element, /* optional */ options) {
  986. /***
  987. Blind an element up: change its vertical size to 0.
  988. ***/
  989. var d = MochiKit.DOM;
  990. element = d.getElement(element);
  991. d.makeClipping(element);
  992. options = MochiKit.Base.update({
  993. scaleContent: false,
  994. scaleX: false,
  995. restoreAfterFinish: true,
  996. afterFinishInternal: function (effect) {
  997. MochiKit.Style.hideElement(effect.element);
  998. d.undoClipping(effect.element);
  999. }
  1000. }, options || {});
  1001. return new MochiKit.Visual.Scale(element, 0, options);
  1002. };
  1003. MochiKit.Visual.blindDown = function (element, /* optional */ options) {
  1004. /***
  1005. Blind an element down: restore its vertical size.
  1006. ***/
  1007. var d = MochiKit.DOM;
  1008. element = d.getElement(element);
  1009. var elementDimensions = MochiKit.Style.getElementDimensions(element);
  1010. options = MochiKit.Base.update({
  1011. scaleContent: false,
  1012. scaleX: false,
  1013. scaleFrom: 0,
  1014. scaleMode: {originalHeight: elementDimensions.h,
  1015. originalWidth: elementDimensions.w},
  1016. restoreAfterFinish: true,
  1017. afterSetupInternal: function (effect) {
  1018. d.makeClipping(effect.element);
  1019. d.setStyle(effect.element, {height: '0px'});
  1020. MochiKit.Style.showElement(effect.element);
  1021. },
  1022. afterFinishInternal: function (effect) {
  1023. d.undoClipping(effect.element);
  1024. }
  1025. }, options || {});
  1026. return new MochiKit.Visual.Scale(element, 100, options);
  1027. };
  1028. MochiKit.Visual.switchOff = function (element, /* optional */ options) {
  1029. /***
  1030. Apply a switch-off-like effect.
  1031. ***/
  1032. var d = MochiKit.DOM;
  1033. element = d.getElement(element);
  1034. var oldOpacity = d.getInlineOpacity(element);
  1035. var options = MochiKit.Base.update({
  1036. duration: 0.3,
  1037. scaleFromCenter: true,
  1038. scaleX: false,
  1039. scaleContent: false,
  1040. restoreAfterFinish: true,
  1041. beforeSetupInternal: function (effect) {
  1042. d.makePositioned(effect.element);
  1043. d.makeClipping(effect.element);
  1044. },
  1045. afterFinishInternal: function (effect) {
  1046. MochiKit.Style.hideElement(effect.element);
  1047. d.undoClipping(effect.element);
  1048. d.undoPositioned(effect.element);
  1049. d.setStyle(effect.element, {opacity: oldOpacity});
  1050. }
  1051. }, options || {});
  1052. var v = MochiKit.Visual;
  1053. return new v.appear(element, {
  1054. duration: 0.4,
  1055. from: 0,
  1056. transition: v.Transitions.flicker,
  1057. afterFinishInternal: function (effect) {
  1058. new v.Scale(effect.element, 1, options)
  1059. }
  1060. });
  1061. };
  1062. MochiKit.Visual.dropOut = function (element, /* optional */ options) {
  1063. /***
  1064. Make an element fall and disappear.
  1065. ***/
  1066. var d = MochiKit.DOM;
  1067. element = d.getElement(element);
  1068. var oldStyle = {
  1069. top: d.getStyle(element, 'top'),
  1070. left: d.getStyle(element, 'left'),
  1071. opacity: d.getInlineOpacity(element)
  1072. };
  1073. options = MochiKit.Base.update({
  1074. duration: 0.5,
  1075. beforeSetupInternal: function (effect) {
  1076. d.makePositioned(effect.effects[0].element);
  1077. },
  1078. afterFinishInternal: function (effect) {
  1079. MochiKit.Style.hideElement(effect.effects[0].element);
  1080. d.undoPositioned(effect.effects[0].element);
  1081. d.setStyle(effect.effects[0].element, oldStyle);
  1082. }
  1083. }, options || {});
  1084. var v = MochiKit.Visual;
  1085. return new v.Parallel(
  1086. [new v.Move(element, {x: 0, y: 100, sync: true}),
  1087. new v.Opacity(element, {sync: true, to: 0.0})],
  1088. options);
  1089. };
  1090. MochiKit.Visual.shake = function (element, /* optional */ options) {
  1091. /***
  1092. Move an element from left to right several times.
  1093. ***/
  1094. var d = MochiKit.DOM;
  1095. var v = MochiKit.Visual;
  1096. element = d.getElement(element);
  1097. options = MochiKit.Base.update({
  1098. x: -20,
  1099. y: 0,
  1100. duration: 0.05,
  1101. afterFinishInternal: function (effect) {
  1102. d.undoPositioned(effect.element);
  1103. d.setStyle(effect.element, oldStyle);
  1104. }
  1105. }, options || {});
  1106. var oldStyle = {
  1107. top: d.getStyle(element, 'top'),
  1108. left: d.getStyle(element, 'left') };
  1109. return new v.Move(element,
  1110. {x: 20, y: 0, duration: 0.05, afterFinishInternal: function (effect) {
  1111. new v.Move(effect.element,
  1112. {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
  1113. new v.Move(effect.element,
  1114. {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
  1115. new v.Move(effect.element,
  1116. {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
  1117. new v.Move(effect.element,
  1118. {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
  1119. new v.Move(effect.element, options
  1120. ) }}) }}) }}) }}) }});
  1121. };
  1122. MochiKit.Visual.slideDown = function (element, /* optional */ options) {
  1123. /***
  1124. Slide an element down.
  1125. It needs to have the content of the element wrapped in a container
  1126. element with fixed height.
  1127. ***/
  1128. var d = MochiKit.DOM;
  1129. var b = MochiKit.Base;
  1130. element = d.getElement(element);
  1131. if (!element.firstChild) {
  1132. throw "MochiKit.Visual.slideDown must be used on a element with a child";
  1133. }
  1134. d.removeEmptyTextNodes(element);
  1135. var oldInnerBottom = d.getStyle(element.firstChild, 'bottom') || 0;
  1136. var elementDimensions = MochiKit.Style.getElementDimensions(element);
  1137. options = b.update({
  1138. scaleContent: false,
  1139. scaleX: false,
  1140. scaleFrom: 0,
  1141. scaleMode: {originalHeight: elementDimensions.h,
  1142. originalWidth: elementDimensions.w},
  1143. restoreAfterFinish: true,
  1144. afterSetupInternal: function (effect) {
  1145. d.makePositioned(effect.element);
  1146. d.makePositioned(effect.element.firstChild);
  1147. if (b.isOpera()) {
  1148. d.setStyle(effect.element, {top: ''});
  1149. }
  1150. d.makeClipping(effect.element);
  1151. d.setStyle(effect.element, {height: '0px'});
  1152. MochiKit.Style.showElement(effect.element);
  1153. },
  1154. afterUpdateInternal: function (effect) {
  1155. d.setStyle(effect.element.firstChild,
  1156. {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'})
  1157. },
  1158. afterFinishInternal: function (effect) {
  1159. d.undoClipping(effect.element);
  1160. // IE will crash if child is undoPositioned first
  1161. if (b.isIE()) {
  1162. d.undoPositioned(effect.element);
  1163. d.undoPositioned(effect.element.firstChild);
  1164. } else {
  1165. d.undoPositioned(effect.element.firstChild);
  1166. d.undoPositioned(effect.element);
  1167. }
  1168. d.setStyle(effect.element.firstChild,
  1169. {bottom: oldInnerBottom});
  1170. }
  1171. }, options || {});
  1172. return new MochiKit.Visual.Scale(element, 100, options);
  1173. };
  1174. MochiKit.Visual.slideUp = function (element, /* optional */ options) {
  1175. /***
  1176. Slide an element up.
  1177. It needs to have the content of the element wrapped in a container
  1178. element with fixed height.
  1179. ***/
  1180. var d = MochiKit.DOM;
  1181. var b = MochiKit.Base;
  1182. element = d.getElement(element);
  1183. if (!element.firstChild) {
  1184. throw "MochiKit.Visual.slideUp must be used on a element with a child";
  1185. }
  1186. d.removeEmptyTextNodes(element);
  1187. var oldInnerBottom = d.getStyle(element.firstChild, 'bottom');
  1188. options = b.update({
  1189. scaleContent: false,
  1190. scaleX: false,
  1191. scaleMode: 'box',
  1192. scaleFrom: 100,
  1193. restoreAfterFinish: true,
  1194. beforeStartInternal: function (effect) {
  1195. d.makePositioned(effect.element);
  1196. d.makePositioned(effect.element.firstChild);
  1197. if (b.isOpera()) {
  1198. d.setStyle(effect.element, {top: ''});
  1199. }
  1200. d.makeClipping(effect.element);
  1201. MochiKit.Style.showElement(effect.element);
  1202. },
  1203. afterUpdateInternal: function (effect) {
  1204. d.setStyle(effect.element.firstChild,
  1205. {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'});
  1206. },
  1207. afterFinishInternal: function (effect) {
  1208. MochiKit.Style.hideElement(effect.element);
  1209. d.undoClipping(effect.element);
  1210. d.undoPositioned(effect.element.firstChild);
  1211. d.undoPositioned(effect.element);
  1212. d.setStyle(effect.element.firstChild, {bottom: oldInnerBottom});
  1213. }
  1214. }, options || {});
  1215. return new MochiKit.Visual.Scale(element, 0, options);
  1216. };
  1217. // Bug in opera makes the TD containing this element expand for a instance
  1218. // after finish
  1219. MochiKit.Visual.squish = function (element, /* optional */ options) {
  1220. /***
  1221. Reduce an element and make it disappear.
  1222. ***/
  1223. var d = MochiKit.DOM;
  1224. var b = MochiKit.Base;
  1225. options = b.update({
  1226. restoreAfterFinish: true,
  1227. beforeSetupInternal: function (effect) {
  1228. d.makeClipping(effect.element);
  1229. },
  1230. afterFinishInternal: function (effect) {
  1231. MochiKit.Style.hideElement(effect.element);
  1232. d.undoClipping(effect.element);
  1233. }
  1234. }, options || {});
  1235. return new MochiKit.Visual.Scale(element, b.isOpera() ? 1 : 0, options);
  1236. };
  1237. MochiKit.Visual.grow = function (element, /* optional */ options) {
  1238. /***
  1239. Grow an element to its original size. Make it zero-sized before
  1240. if necessary.
  1241. ***/
  1242. var d = MochiKit.DOM;
  1243. var v = MochiKit.Visual;
  1244. element = d.getElement(element);
  1245. options = MochiKit.Base.update({
  1246. direction: 'center',
  1247. moveTransition: v.Transitions.sinoidal,
  1248. scaleTransition: v.Transitions.sinoidal,
  1249. opacityTransition: v.Transitions.full
  1250. }, options || {});
  1251. var oldStyle = {
  1252. top: element.style.top,
  1253. left: element.style.left,
  1254. height: element.style.height,
  1255. width: element.style.width,
  1256. opacity: d.getInlineOpacity(element)
  1257. };
  1258. var dims = MochiKit.Style.getElementDimensions(element);
  1259. var initialMoveX, initialMoveY;
  1260. var moveX, moveY;
  1261. switch (options.direction) {
  1262. case 'top-left':
  1263. initialMoveX = initialMoveY = moveX = moveY = 0;
  1264. break;
  1265. case 'top-right':
  1266. initialMoveX = dims.w;
  1267. initialMoveY = moveY = 0;
  1268. moveX = -dims.w;
  1269. break;
  1270. case 'bottom-left':
  1271. initialMoveX = moveX = 0;
  1272. initialMoveY = dims.h;
  1273. moveY = -dims.h;
  1274. break;
  1275. case 'bottom-right':
  1276. initialMoveX = dims.w;
  1277. initialMoveY = dims.h;
  1278. moveX = -dims.w;
  1279. moveY = -dims.h;
  1280. break;
  1281. case 'center':
  1282. initialMoveX = dims.w / 2;
  1283. initialMoveY = dims.h / 2;
  1284. moveX = -dims.w / 2;
  1285. moveY = -dims.h / 2;
  1286. break;
  1287. }
  1288. var optionsParallel = MochiKit.Base.update({
  1289. beforeSetupInternal: function (effect) {
  1290. d.setStyle(effect.effects[0].element, {height: '0px'});
  1291. MochiKit.Style.showElement(effect.effects[0].element);
  1292. },
  1293. afterFinishInternal: function (effect) {
  1294. d.undoClipping(effect.effects[0].element);
  1295. d.undoPositioned(effect.effects[0].element);
  1296. d.setStyle(effect.effects[0].element, oldStyle);
  1297. }
  1298. }, options || {});
  1299. return new v.Move(element, {
  1300. x: initialMoveX,
  1301. y: initialMoveY,
  1302. duration: 0.01,
  1303. beforeSetupInternal: function (effect) {
  1304. MochiKit.Style.hideElement(effect.element);
  1305. d.makeClipping(effect.element);
  1306. d.makePositioned(effect.element);
  1307. },
  1308. afterFinishInternal: function (effect) {
  1309. new v.Parallel(
  1310. [new v.Opacity(effect.element, {
  1311. sync: true, to: 1.0, from: 0.0,
  1312. transition: options.opacityTransition
  1313. }),
  1314. new v.Move(effect.element, {
  1315. x: moveX, y: moveY, sync: true,
  1316. transition: options.moveTransition
  1317. }),
  1318. new v.Scale(effect.element, 100, {
  1319. scaleMode: {originalHeight: dims.h,
  1320. originalWidth: dims.w},
  1321. sync: true,
  1322. scaleFrom: MochiKit.Base.isOpera() ? 1 : 0,
  1323. transition: options.scaleTransition,
  1324. restoreAfterFinish: true
  1325. })
  1326. ], optionsParallel
  1327. );
  1328. }
  1329. });
  1330. };
  1331. MochiKit.Visual.shrink = function (element, /* optional */ options) {
  1332. /***
  1333. Shrink an element and make it disappear.
  1334. ***/
  1335. var d = MochiKit.DOM;
  1336. var v = MochiKit.Visual;
  1337. element = d.getElement(element);
  1338. options = MochiKit.Base.update({
  1339. direction: 'center',
  1340. moveTransition: v.Transitions.sinoidal,
  1341. scaleTransition: v.Transitions.sinoidal,
  1342. opacityTransition: v.Transitions.none
  1343. }, options || {});
  1344. var oldStyle = {
  1345. top: element.style.top,
  1346. left: element.style.left,
  1347. height: element.style.height,
  1348. width: element.style.width,
  1349. opacity: d.getInlineOpacity(element)
  1350. };
  1351. var dims = MochiKit.Style.getElementDimensions(element);
  1352. var moveX, moveY;
  1353. switch (options.direction) {
  1354. case 'top-left':
  1355. moveX = moveY = 0;
  1356. break;
  1357. case 'top-right':
  1358. moveX = dims.w;
  1359. moveY = 0;
  1360. break;
  1361. case 'bottom-left':
  1362. moveX = 0;
  1363. moveY = dims.h;
  1364. break;
  1365. case 'bottom-right':
  1366. moveX = dims.w;
  1367. moveY = dims.h;
  1368. break;
  1369. case 'center':
  1370. moveX = dims.w / 2;
  1371. moveY = dims.h / 2;
  1372. break;
  1373. }
  1374. var optionsParallel = MochiKit.Base.update({
  1375. beforeStartInternal: function (effect) {
  1376. d.makePositioned(effect.effects[0].element);
  1377. d.makeClipping(effect.effects[0].element);
  1378. },
  1379. afterFinishInternal: function (effect) {
  1380. MochiKit.Style.hideElement(effect.effects[0].element);
  1381. d.undoClipping(effect.effects[0].element);
  1382. d.undoPositioned(effect.effects[0].element);
  1383. d.setStyle(effect.effects[0].element, oldStyle);
  1384. }
  1385. }, options || {});
  1386. return new v.Parallel(
  1387. [new v.Opacity(element, {
  1388. sync: true, to: 0.0, from: 1.0,
  1389. transition: options.opacityTransition
  1390. }),
  1391. new v.Scale(element, MochiKit.Base.isOpera() ? 1 : 0, {
  1392. sync: true, transition: options.scaleTransition,
  1393. restoreAfterFinish: true
  1394. }),
  1395. new v.Move(element, {
  1396. x: moveX, y: moveY, sync: true, transition: options.moveTransition
  1397. })
  1398. ], optionsParallel
  1399. );
  1400. };
  1401. MochiKit.Visual.pulsate = function (element, /* optional */ options) {
  1402. /***
  1403. Pulse an element between appear/fade.
  1404. ***/
  1405. var d = MochiKit.DOM;
  1406. var v = MochiKit.Visual;
  1407. var b = MochiKit.Base;
  1408. element = d.getElement(element);
  1409. var oldOpacity = d.getInlineOpacity(element);
  1410. options = b.update({
  1411. duration: 3.0,
  1412. from: 0,
  1413. afterFinishInternal: function (effect) {
  1414. d.setStyle(effect.element, {opacity: oldOpacity});
  1415. }
  1416. }, options || {});
  1417. var transition = options.transition || v.Transitions.sinoidal;
  1418. var reverser = b.bind(function (pos) {
  1419. return transition(1 - v.Transitions.pulse(pos));
  1420. }, transition);
  1421. b.bind(reverser, transition);
  1422. return new v.Opacity(element, b.update({
  1423. transition: reverser}, options));
  1424. };
  1425. MochiKit.Visual.fold = function (element, /* optional */ options) {
  1426. /***
  1427. Fold an element, first vertically, then horizontally.
  1428. ***/
  1429. var d = MochiKit.DOM;
  1430. var v = MochiKit.Visual;
  1431. element = d.getElement(element);
  1432. var oldStyle = {
  1433. top: element.style.top,
  1434. left: element.style.left,
  1435. width: element.style.width,
  1436. height: element.style.height
  1437. };
  1438. d.makeClipping(element);
  1439. options = MochiKit.Base.update({
  1440. scaleContent: false,
  1441. scaleX: false,
  1442. afterFinishInternal: function (effect) {
  1443. new v.Scale(element, 1, {
  1444. scaleContent: false,
  1445. scaleY: false,
  1446. afterFinishInternal: function (effect) {
  1447. MochiKit.Style.hideElement(effect.element);
  1448. d.undoClipping(effect.element);
  1449. d.setStyle(effect.element, oldStyle);
  1450. }
  1451. });
  1452. }
  1453. }, options || {});
  1454. return new v.Scale(element, 5, options);
  1455. };
  1456. // Compatibility with MochiKit 1.0
  1457. MochiKit.Visual.Color = MochiKit.Color.Color;
  1458. MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle;
  1459. /* end of Rico adaptation */
  1460. MochiKit.Visual.__new__ = function () {
  1461. var m = MochiKit.Base;
  1462. m.nameFunctions(this);
  1463. this.EXPORT_TAGS = {
  1464. ":common": this.EXPORT,
  1465. ":all": m.concat(this.EXPORT, this.EXPORT_OK)
  1466. };
  1467. };
  1468. MochiKit.Visual.EXPORT = [
  1469. "roundElement",
  1470. "roundClass",
  1471. "tagifyText",
  1472. "multiple",
  1473. "toggle",
  1474. "Base",
  1475. "Parallel",
  1476. "Opacity",
  1477. "Move",
  1478. "Scale",
  1479. "Highlight",
  1480. "ScrollTo",
  1481. "fade",
  1482. "appear",
  1483. "puff",
  1484. "blindUp",
  1485. "blindDown",
  1486. "switchOff",
  1487. "dropOut",
  1488. "shake",
  1489. "slideDown",
  1490. "slideUp",
  1491. "squish",
  1492. "grow",
  1493. "shrink",
  1494. "pulsate",
  1495. "fold"
  1496. ];
  1497. MochiKit.Visual.EXPORT_OK = [
  1498. "PAIRS"
  1499. ];
  1500. MochiKit.Visual.__new__();
  1501. MochiKit.Base._exportSymbols(this, MochiKit.Visual);