Visual.js 55 KB

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