alpine.js 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alpine = factory());
  5. }(this, (function () { 'use strict';
  6. function _defineProperty(obj, key, value) {
  7. if (key in obj) {
  8. Object.defineProperty(obj, key, {
  9. value: value,
  10. enumerable: true,
  11. configurable: true,
  12. writable: true
  13. });
  14. } else {
  15. obj[key] = value;
  16. }
  17. return obj;
  18. }
  19. function ownKeys(object, enumerableOnly) {
  20. var keys = Object.keys(object);
  21. if (Object.getOwnPropertySymbols) {
  22. var symbols = Object.getOwnPropertySymbols(object);
  23. if (enumerableOnly) symbols = symbols.filter(function (sym) {
  24. return Object.getOwnPropertyDescriptor(object, sym).enumerable;
  25. });
  26. keys.push.apply(keys, symbols);
  27. }
  28. return keys;
  29. }
  30. function _objectSpread2(target) {
  31. for (var i = 1; i < arguments.length; i++) {
  32. var source = arguments[i] != null ? arguments[i] : {};
  33. if (i % 2) {
  34. ownKeys(Object(source), true).forEach(function (key) {
  35. _defineProperty(target, key, source[key]);
  36. });
  37. } else if (Object.getOwnPropertyDescriptors) {
  38. Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
  39. } else {
  40. ownKeys(Object(source)).forEach(function (key) {
  41. Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
  42. });
  43. }
  44. }
  45. return target;
  46. }
  47. // Thanks @stimulus:
  48. // https://github.com/stimulusjs/stimulus/blob/master/packages/%40stimulus/core/src/application.ts
  49. function domReady() {
  50. return new Promise(resolve => {
  51. if (document.readyState == "loading") {
  52. document.addEventListener("DOMContentLoaded", resolve);
  53. } else {
  54. resolve();
  55. }
  56. });
  57. }
  58. function arrayUnique(array) {
  59. return Array.from(new Set(array));
  60. }
  61. function isTesting() {
  62. return navigator.userAgent.includes("Node.js") || navigator.userAgent.includes("jsdom");
  63. }
  64. function checkedAttrLooseCompare(valueA, valueB) {
  65. return valueA == valueB;
  66. }
  67. function warnIfMalformedTemplate(el, directive) {
  68. if (el.tagName.toLowerCase() !== 'template') {
  69. console.warn(`Alpine: [${directive}] directive should only be added to <template> tags. See https://github.com/alpinejs/alpine#${directive}`);
  70. } else if (el.content.childElementCount !== 1) {
  71. console.warn(`Alpine: <template> tag with [${directive}] encountered with an unexpected number of root elements. Make sure <template> has a single root element. `);
  72. }
  73. }
  74. function kebabCase(subject) {
  75. return subject.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[_\s]/, '-').toLowerCase();
  76. }
  77. function camelCase(subject) {
  78. return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase());
  79. }
  80. function walk(el, callback) {
  81. if (callback(el) === false) return;
  82. let node = el.firstElementChild;
  83. while (node) {
  84. walk(node, callback);
  85. node = node.nextElementSibling;
  86. }
  87. }
  88. function debounce(func, wait) {
  89. var timeout;
  90. return function () {
  91. var context = this,
  92. args = arguments;
  93. var later = function later() {
  94. timeout = null;
  95. func.apply(context, args);
  96. };
  97. clearTimeout(timeout);
  98. timeout = setTimeout(later, wait);
  99. };
  100. }
  101. const handleError = (el, expression, error) => {
  102. console.warn(`Alpine Error: "${error}"\n\nExpression: "${expression}"\nElement:`, el);
  103. if (!isTesting()) {
  104. Object.assign(error, {
  105. el,
  106. expression
  107. });
  108. throw error;
  109. }
  110. };
  111. function tryCatch(cb, {
  112. el,
  113. expression
  114. }) {
  115. try {
  116. const value = cb();
  117. return value instanceof Promise ? value.catch(e => handleError(el, expression, e)) : value;
  118. } catch (e) {
  119. handleError(el, expression, e);
  120. }
  121. }
  122. function saferEval(el, expression, dataContext, additionalHelperVariables = {}) {
  123. return tryCatch(() => {
  124. if (typeof expression === 'function') {
  125. return expression.call(dataContext);
  126. }
  127. return new Function(['$data', ...Object.keys(additionalHelperVariables)], `var __alpine_result; with($data) { __alpine_result = ${expression} }; return __alpine_result`)(dataContext, ...Object.values(additionalHelperVariables));
  128. }, {
  129. el,
  130. expression
  131. });
  132. }
  133. function saferEvalNoReturn(el, expression, dataContext, additionalHelperVariables = {}) {
  134. return tryCatch(() => {
  135. if (typeof expression === 'function') {
  136. return Promise.resolve(expression.call(dataContext, additionalHelperVariables['$event']));
  137. }
  138. let AsyncFunction = Function;
  139. /* MODERN-ONLY:START */
  140. AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
  141. /* MODERN-ONLY:END */
  142. // For the cases when users pass only a function reference to the caller: `x-on:click="foo"`
  143. // Where "foo" is a function. Also, we'll pass the function the event instance when we call it.
  144. if (Object.keys(dataContext).includes(expression)) {
  145. let methodReference = new Function(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { return ${expression} }`)(dataContext, ...Object.values(additionalHelperVariables));
  146. if (typeof methodReference === 'function') {
  147. return Promise.resolve(methodReference.call(dataContext, additionalHelperVariables['$event']));
  148. } else {
  149. return Promise.resolve();
  150. }
  151. }
  152. return Promise.resolve(new AsyncFunction(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { ${expression} }`)(dataContext, ...Object.values(additionalHelperVariables)));
  153. }, {
  154. el,
  155. expression
  156. });
  157. }
  158. const xAttrRE = /^x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref|spread)\b/;
  159. function isXAttr(attr) {
  160. const name = replaceAtAndColonWithStandardSyntax(attr.name);
  161. return xAttrRE.test(name);
  162. }
  163. function getXAttrs(el, component, type) {
  164. let directives = Array.from(el.attributes).filter(isXAttr).map(parseHtmlAttribute); // Get an object of directives from x-spread.
  165. let spreadDirective = directives.filter(directive => directive.type === 'spread')[0];
  166. if (spreadDirective) {
  167. let spreadObject = saferEval(el, spreadDirective.expression, component.$data); // Add x-spread directives to the pile of existing directives.
  168. directives = directives.concat(Object.entries(spreadObject).map(([name, value]) => parseHtmlAttribute({
  169. name,
  170. value
  171. })));
  172. }
  173. if (type) return directives.filter(i => i.type === type);
  174. return sortDirectives(directives);
  175. }
  176. function sortDirectives(directives) {
  177. let directiveOrder = ['bind', 'model', 'show', 'catch-all'];
  178. return directives.sort((a, b) => {
  179. let typeA = directiveOrder.indexOf(a.type) === -1 ? 'catch-all' : a.type;
  180. let typeB = directiveOrder.indexOf(b.type) === -1 ? 'catch-all' : b.type;
  181. return directiveOrder.indexOf(typeA) - directiveOrder.indexOf(typeB);
  182. });
  183. }
  184. function parseHtmlAttribute({
  185. name,
  186. value
  187. }) {
  188. const normalizedName = replaceAtAndColonWithStandardSyntax(name);
  189. const typeMatch = normalizedName.match(xAttrRE);
  190. const valueMatch = normalizedName.match(/:([a-zA-Z0-9\-:]+)/);
  191. const modifiers = normalizedName.match(/\.[^.\]]+(?=[^\]]*$)/g) || [];
  192. return {
  193. type: typeMatch ? typeMatch[1] : null,
  194. value: valueMatch ? valueMatch[1] : null,
  195. modifiers: modifiers.map(i => i.replace('.', '')),
  196. expression: value
  197. };
  198. }
  199. function isBooleanAttr(attrName) {
  200. // As per HTML spec table https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute
  201. // Array roughly ordered by estimated usage
  202. const booleanAttributes = ['disabled', 'checked', 'required', 'readonly', 'hidden', 'open', 'selected', 'autofocus', 'itemscope', 'multiple', 'novalidate', 'allowfullscreen', 'allowpaymentrequest', 'formnovalidate', 'autoplay', 'controls', 'loop', 'muted', 'playsinline', 'default', 'ismap', 'reversed', 'async', 'defer', 'nomodule'];
  203. return booleanAttributes.includes(attrName);
  204. }
  205. function replaceAtAndColonWithStandardSyntax(name) {
  206. if (name.startsWith('@')) {
  207. return name.replace('@', 'x-on:');
  208. } else if (name.startsWith(':')) {
  209. return name.replace(':', 'x-bind:');
  210. }
  211. return name;
  212. }
  213. function convertClassStringToArray(classList, filterFn = Boolean) {
  214. return classList.split(' ').filter(filterFn);
  215. }
  216. const TRANSITION_TYPE_IN = 'in';
  217. const TRANSITION_TYPE_OUT = 'out';
  218. const TRANSITION_CANCELLED = 'cancelled';
  219. function transitionIn(el, show, reject, component, forceSkip = false) {
  220. // We don't want to transition on the initial page load.
  221. if (forceSkip) return show();
  222. if (el.__x_transition && el.__x_transition.type === TRANSITION_TYPE_IN) {
  223. // there is already a similar transition going on, this was probably triggered by
  224. // a change in a different property, let's just leave the previous one doing its job
  225. return;
  226. }
  227. const attrs = getXAttrs(el, component, 'transition');
  228. const showAttr = getXAttrs(el, component, 'show')[0]; // If this is triggered by a x-show.transition.
  229. if (showAttr && showAttr.modifiers.includes('transition')) {
  230. let modifiers = showAttr.modifiers; // If x-show.transition.out, we'll skip the "in" transition.
  231. if (modifiers.includes('out') && !modifiers.includes('in')) return show();
  232. const settingBothSidesOfTransition = modifiers.includes('in') && modifiers.includes('out'); // If x-show.transition.in...out... only use "in" related modifiers for this transition.
  233. modifiers = settingBothSidesOfTransition ? modifiers.filter((i, index) => index < modifiers.indexOf('out')) : modifiers;
  234. transitionHelperIn(el, modifiers, show, reject); // Otherwise, we can assume x-transition:enter.
  235. } else if (attrs.some(attr => ['enter', 'enter-start', 'enter-end'].includes(attr.value))) {
  236. transitionClassesIn(el, component, attrs, show, reject);
  237. } else {
  238. // If neither, just show that damn thing.
  239. show();
  240. }
  241. }
  242. function transitionOut(el, hide, reject, component, forceSkip = false) {
  243. // We don't want to transition on the initial page load.
  244. if (forceSkip) return hide();
  245. if (el.__x_transition && el.__x_transition.type === TRANSITION_TYPE_OUT) {
  246. // there is already a similar transition going on, this was probably triggered by
  247. // a change in a different property, let's just leave the previous one doing its job
  248. return;
  249. }
  250. const attrs = getXAttrs(el, component, 'transition');
  251. const showAttr = getXAttrs(el, component, 'show')[0];
  252. if (showAttr && showAttr.modifiers.includes('transition')) {
  253. let modifiers = showAttr.modifiers;
  254. if (modifiers.includes('in') && !modifiers.includes('out')) return hide();
  255. const settingBothSidesOfTransition = modifiers.includes('in') && modifiers.includes('out');
  256. modifiers = settingBothSidesOfTransition ? modifiers.filter((i, index) => index > modifiers.indexOf('out')) : modifiers;
  257. transitionHelperOut(el, modifiers, settingBothSidesOfTransition, hide, reject);
  258. } else if (attrs.some(attr => ['leave', 'leave-start', 'leave-end'].includes(attr.value))) {
  259. transitionClassesOut(el, component, attrs, hide, reject);
  260. } else {
  261. hide();
  262. }
  263. }
  264. function transitionHelperIn(el, modifiers, showCallback, reject) {
  265. // Default values inspired by: https://material.io/design/motion/speed.html#duration
  266. const styleValues = {
  267. duration: modifierValue(modifiers, 'duration', 150),
  268. origin: modifierValue(modifiers, 'origin', 'center'),
  269. first: {
  270. opacity: 0,
  271. scale: modifierValue(modifiers, 'scale', 95)
  272. },
  273. second: {
  274. opacity: 1,
  275. scale: 100
  276. }
  277. };
  278. transitionHelper(el, modifiers, showCallback, () => {}, reject, styleValues, TRANSITION_TYPE_IN);
  279. }
  280. function transitionHelperOut(el, modifiers, settingBothSidesOfTransition, hideCallback, reject) {
  281. // Make the "out" transition .5x slower than the "in". (Visually better)
  282. // HOWEVER, if they explicitly set a duration for the "out" transition,
  283. // use that.
  284. const duration = settingBothSidesOfTransition ? modifierValue(modifiers, 'duration', 150) : modifierValue(modifiers, 'duration', 150) / 2;
  285. const styleValues = {
  286. duration: duration,
  287. origin: modifierValue(modifiers, 'origin', 'center'),
  288. first: {
  289. opacity: 1,
  290. scale: 100
  291. },
  292. second: {
  293. opacity: 0,
  294. scale: modifierValue(modifiers, 'scale', 95)
  295. }
  296. };
  297. transitionHelper(el, modifiers, () => {}, hideCallback, reject, styleValues, TRANSITION_TYPE_OUT);
  298. }
  299. function modifierValue(modifiers, key, fallback) {
  300. // If the modifier isn't present, use the default.
  301. if (modifiers.indexOf(key) === -1) return fallback; // If it IS present, grab the value after it: x-show.transition.duration.500ms
  302. const rawValue = modifiers[modifiers.indexOf(key) + 1];
  303. if (!rawValue) return fallback;
  304. if (key === 'scale') {
  305. // Check if the very next value is NOT a number and return the fallback.
  306. // If x-show.transition.scale, we'll use the default scale value.
  307. // That is how a user opts out of the opacity transition.
  308. if (!isNumeric(rawValue)) return fallback;
  309. }
  310. if (key === 'duration') {
  311. // Support x-show.transition.duration.500ms && duration.500
  312. let match = rawValue.match(/([0-9]+)ms/);
  313. if (match) return match[1];
  314. }
  315. if (key === 'origin') {
  316. // Support chaining origin directions: x-show.transition.top.right
  317. if (['top', 'right', 'left', 'center', 'bottom'].includes(modifiers[modifiers.indexOf(key) + 2])) {
  318. return [rawValue, modifiers[modifiers.indexOf(key) + 2]].join(' ');
  319. }
  320. }
  321. return rawValue;
  322. }
  323. function transitionHelper(el, modifiers, hook1, hook2, reject, styleValues, type) {
  324. // clear the previous transition if exists to avoid caching the wrong styles
  325. if (el.__x_transition) {
  326. el.__x_transition.cancel && el.__x_transition.cancel();
  327. } // If the user set these style values, we'll put them back when we're done with them.
  328. const opacityCache = el.style.opacity;
  329. const transformCache = el.style.transform;
  330. const transformOriginCache = el.style.transformOrigin; // If no modifiers are present: x-show.transition, we'll default to both opacity and scale.
  331. const noModifiers = !modifiers.includes('opacity') && !modifiers.includes('scale');
  332. const transitionOpacity = noModifiers || modifiers.includes('opacity');
  333. const transitionScale = noModifiers || modifiers.includes('scale'); // These are the explicit stages of a transition (same stages for in and for out).
  334. // This way you can get a birds eye view of the hooks, and the differences
  335. // between them.
  336. const stages = {
  337. start() {
  338. if (transitionOpacity) el.style.opacity = styleValues.first.opacity;
  339. if (transitionScale) el.style.transform = `scale(${styleValues.first.scale / 100})`;
  340. },
  341. during() {
  342. if (transitionScale) el.style.transformOrigin = styleValues.origin;
  343. el.style.transitionProperty = [transitionOpacity ? `opacity` : ``, transitionScale ? `transform` : ``].join(' ').trim();
  344. el.style.transitionDuration = `${styleValues.duration / 1000}s`;
  345. el.style.transitionTimingFunction = `cubic-bezier(0.4, 0.0, 0.2, 1)`;
  346. },
  347. show() {
  348. hook1();
  349. },
  350. end() {
  351. if (transitionOpacity) el.style.opacity = styleValues.second.opacity;
  352. if (transitionScale) el.style.transform = `scale(${styleValues.second.scale / 100})`;
  353. },
  354. hide() {
  355. hook2();
  356. },
  357. cleanup() {
  358. if (transitionOpacity) el.style.opacity = opacityCache;
  359. if (transitionScale) el.style.transform = transformCache;
  360. if (transitionScale) el.style.transformOrigin = transformOriginCache;
  361. el.style.transitionProperty = null;
  362. el.style.transitionDuration = null;
  363. el.style.transitionTimingFunction = null;
  364. }
  365. };
  366. transition(el, stages, type, reject);
  367. }
  368. const ensureStringExpression = (expression, el, component) => {
  369. return typeof expression === 'function' ? component.evaluateReturnExpression(el, expression) : expression;
  370. };
  371. function transitionClassesIn(el, component, directives, showCallback, reject) {
  372. const enter = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'enter') || {
  373. expression: ''
  374. }).expression, el, component));
  375. const enterStart = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'enter-start') || {
  376. expression: ''
  377. }).expression, el, component));
  378. const enterEnd = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'enter-end') || {
  379. expression: ''
  380. }).expression, el, component));
  381. transitionClasses(el, enter, enterStart, enterEnd, showCallback, () => {}, TRANSITION_TYPE_IN, reject);
  382. }
  383. function transitionClassesOut(el, component, directives, hideCallback, reject) {
  384. const leave = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'leave') || {
  385. expression: ''
  386. }).expression, el, component));
  387. const leaveStart = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'leave-start') || {
  388. expression: ''
  389. }).expression, el, component));
  390. const leaveEnd = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'leave-end') || {
  391. expression: ''
  392. }).expression, el, component));
  393. transitionClasses(el, leave, leaveStart, leaveEnd, () => {}, hideCallback, TRANSITION_TYPE_OUT, reject);
  394. }
  395. function transitionClasses(el, classesDuring, classesStart, classesEnd, hook1, hook2, type, reject) {
  396. // clear the previous transition if exists to avoid caching the wrong classes
  397. if (el.__x_transition) {
  398. el.__x_transition.cancel && el.__x_transition.cancel();
  399. }
  400. const originalClasses = el.__x_original_classes || [];
  401. const stages = {
  402. start() {
  403. el.classList.add(...classesStart);
  404. },
  405. during() {
  406. el.classList.add(...classesDuring);
  407. },
  408. show() {
  409. hook1();
  410. },
  411. end() {
  412. // Don't remove classes that were in the original class attribute.
  413. el.classList.remove(...classesStart.filter(i => !originalClasses.includes(i)));
  414. el.classList.add(...classesEnd);
  415. },
  416. hide() {
  417. hook2();
  418. },
  419. cleanup() {
  420. el.classList.remove(...classesDuring.filter(i => !originalClasses.includes(i)));
  421. el.classList.remove(...classesEnd.filter(i => !originalClasses.includes(i)));
  422. }
  423. };
  424. transition(el, stages, type, reject);
  425. }
  426. function transition(el, stages, type, reject) {
  427. const finish = once(() => {
  428. stages.hide(); // Adding an "isConnected" check, in case the callback
  429. // removed the element from the DOM.
  430. if (el.isConnected) {
  431. stages.cleanup();
  432. }
  433. delete el.__x_transition;
  434. });
  435. el.__x_transition = {
  436. // Set transition type so we can avoid clearing transition if the direction is the same
  437. type: type,
  438. // create a callback for the last stages of the transition so we can call it
  439. // from different point and early terminate it. Once will ensure that function
  440. // is only called one time.
  441. cancel: once(() => {
  442. reject(TRANSITION_CANCELLED);
  443. finish();
  444. }),
  445. finish,
  446. // This store the next animation frame so we can cancel it
  447. nextFrame: null
  448. };
  449. stages.start();
  450. stages.during();
  451. el.__x_transition.nextFrame = requestAnimationFrame(() => {
  452. // Note: Safari's transitionDuration property will list out comma separated transition durations
  453. // for every single transition property. Let's grab the first one and call it a day.
  454. let duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, '').replace('s', '')) * 1000;
  455. if (duration === 0) {
  456. duration = Number(getComputedStyle(el).animationDuration.replace('s', '')) * 1000;
  457. }
  458. stages.show();
  459. el.__x_transition.nextFrame = requestAnimationFrame(() => {
  460. stages.end();
  461. setTimeout(el.__x_transition.finish, duration);
  462. });
  463. });
  464. }
  465. function isNumeric(subject) {
  466. return !Array.isArray(subject) && !isNaN(subject);
  467. } // Thanks @vuejs
  468. // https://github.com/vuejs/vue/blob/4de4649d9637262a9b007720b59f80ac72a5620c/src/shared/util.js
  469. function once(callback) {
  470. let called = false;
  471. return function () {
  472. if (!called) {
  473. called = true;
  474. callback.apply(this, arguments);
  475. }
  476. };
  477. }
  478. function handleForDirective(component, templateEl, expression, initialUpdate, extraVars) {
  479. warnIfMalformedTemplate(templateEl, 'x-for');
  480. let iteratorNames = typeof expression === 'function' ? parseForExpression(component.evaluateReturnExpression(templateEl, expression)) : parseForExpression(expression);
  481. let items = evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, templateEl, iteratorNames, extraVars); // As we walk the array, we'll also walk the DOM (updating/creating as we go).
  482. let currentEl = templateEl;
  483. items.forEach((item, index) => {
  484. let iterationScopeVariables = getIterationScopeVariables(iteratorNames, item, index, items, extraVars());
  485. let currentKey = generateKeyForIteration(component, templateEl, index, iterationScopeVariables);
  486. let nextEl = lookAheadForMatchingKeyedElementAndMoveItIfFound(currentEl.nextElementSibling, currentKey); // If we haven't found a matching key, insert the element at the current position.
  487. if (!nextEl) {
  488. nextEl = addElementInLoopAfterCurrentEl(templateEl, currentEl); // And transition it in if it's not the first page load.
  489. transitionIn(nextEl, () => {}, () => {}, component, initialUpdate);
  490. nextEl.__x_for = iterationScopeVariables;
  491. component.initializeElements(nextEl, () => nextEl.__x_for); // Otherwise update the element we found.
  492. } else {
  493. // Temporarily remove the key indicator to allow the normal "updateElements" to work.
  494. delete nextEl.__x_for_key;
  495. nextEl.__x_for = iterationScopeVariables;
  496. component.updateElements(nextEl, () => nextEl.__x_for);
  497. }
  498. currentEl = nextEl;
  499. currentEl.__x_for_key = currentKey;
  500. });
  501. removeAnyLeftOverElementsFromPreviousUpdate(currentEl, component);
  502. } // This was taken from VueJS 2.* core. Thanks Vue!
  503. function parseForExpression(expression) {
  504. let forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
  505. let stripParensRE = /^\(|\)$/g;
  506. let forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
  507. let inMatch = String(expression).match(forAliasRE);
  508. if (!inMatch) return;
  509. let res = {};
  510. res.items = inMatch[2].trim();
  511. let item = inMatch[1].trim().replace(stripParensRE, '');
  512. let iteratorMatch = item.match(forIteratorRE);
  513. if (iteratorMatch) {
  514. res.item = item.replace(forIteratorRE, '').trim();
  515. res.index = iteratorMatch[1].trim();
  516. if (iteratorMatch[2]) {
  517. res.collection = iteratorMatch[2].trim();
  518. }
  519. } else {
  520. res.item = item;
  521. }
  522. return res;
  523. }
  524. function getIterationScopeVariables(iteratorNames, item, index, items, extraVars) {
  525. // We must create a new object, so each iteration has a new scope
  526. let scopeVariables = extraVars ? _objectSpread2({}, extraVars) : {};
  527. scopeVariables[iteratorNames.item] = item;
  528. if (iteratorNames.index) scopeVariables[iteratorNames.index] = index;
  529. if (iteratorNames.collection) scopeVariables[iteratorNames.collection] = items;
  530. return scopeVariables;
  531. }
  532. function generateKeyForIteration(component, el, index, iterationScopeVariables) {
  533. let bindKeyAttribute = getXAttrs(el, component, 'bind').filter(attr => attr.value === 'key')[0]; // If the dev hasn't specified a key, just return the index of the iteration.
  534. if (!bindKeyAttribute) return index;
  535. return component.evaluateReturnExpression(el, bindKeyAttribute.expression, () => iterationScopeVariables);
  536. }
  537. function evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, el, iteratorNames, extraVars) {
  538. let ifAttribute = getXAttrs(el, component, 'if')[0];
  539. if (ifAttribute && !component.evaluateReturnExpression(el, ifAttribute.expression)) {
  540. return [];
  541. }
  542. let items = component.evaluateReturnExpression(el, iteratorNames.items, extraVars); // This adds support for the `i in n` syntax.
  543. if (isNumeric(items) && items >= 0) {
  544. items = Array.from(Array(items).keys(), i => i + 1);
  545. }
  546. return items;
  547. }
  548. function addElementInLoopAfterCurrentEl(templateEl, currentEl) {
  549. let clone = document.importNode(templateEl.content, true);
  550. currentEl.parentElement.insertBefore(clone, currentEl.nextElementSibling);
  551. return currentEl.nextElementSibling;
  552. }
  553. function lookAheadForMatchingKeyedElementAndMoveItIfFound(nextEl, currentKey) {
  554. if (!nextEl) return; // If we are already past the x-for generated elements, we don't need to look ahead.
  555. if (nextEl.__x_for_key === undefined) return; // If the the key's DO match, no need to look ahead.
  556. if (nextEl.__x_for_key === currentKey) return nextEl; // If they don't, we'll look ahead for a match.
  557. // If we find it, we'll move it to the current position in the loop.
  558. let tmpNextEl = nextEl;
  559. while (tmpNextEl) {
  560. if (tmpNextEl.__x_for_key === currentKey) {
  561. return tmpNextEl.parentElement.insertBefore(tmpNextEl, nextEl);
  562. }
  563. tmpNextEl = tmpNextEl.nextElementSibling && tmpNextEl.nextElementSibling.__x_for_key !== undefined ? tmpNextEl.nextElementSibling : false;
  564. }
  565. }
  566. function removeAnyLeftOverElementsFromPreviousUpdate(currentEl, component) {
  567. var nextElementFromOldLoop = currentEl.nextElementSibling && currentEl.nextElementSibling.__x_for_key !== undefined ? currentEl.nextElementSibling : false;
  568. while (nextElementFromOldLoop) {
  569. let nextElementFromOldLoopImmutable = nextElementFromOldLoop;
  570. let nextSibling = nextElementFromOldLoop.nextElementSibling;
  571. transitionOut(nextElementFromOldLoop, () => {
  572. nextElementFromOldLoopImmutable.remove();
  573. }, () => {}, component);
  574. nextElementFromOldLoop = nextSibling && nextSibling.__x_for_key !== undefined ? nextSibling : false;
  575. }
  576. }
  577. function handleAttributeBindingDirective(component, el, attrName, expression, extraVars, attrType, modifiers) {
  578. var value = component.evaluateReturnExpression(el, expression, extraVars);
  579. if (attrName === 'value') {
  580. if (Alpine.ignoreFocusedForValueBinding && document.activeElement.isSameNode(el)) return; // If nested model key is undefined, set the default value to empty string.
  581. if (value === undefined && String(expression).match(/\./)) {
  582. value = '';
  583. }
  584. if (el.type === 'radio') {
  585. // Set radio value from x-bind:value, if no "value" attribute exists.
  586. // If there are any initial state values, radio will have a correct
  587. // "checked" value since x-bind:value is processed before x-model.
  588. if (el.attributes.value === undefined && attrType === 'bind') {
  589. el.value = value;
  590. } else if (attrType !== 'bind') {
  591. el.checked = checkedAttrLooseCompare(el.value, value);
  592. }
  593. } else if (el.type === 'checkbox') {
  594. // If we are explicitly binding a string to the :value, set the string,
  595. // If the value is a boolean, leave it alone, it will be set to "on"
  596. // automatically.
  597. if (typeof value !== 'boolean' && ![null, undefined].includes(value) && attrType === 'bind') {
  598. el.value = String(value);
  599. } else if (attrType !== 'bind') {
  600. if (Array.isArray(value)) {
  601. // I'm purposely not using Array.includes here because it's
  602. // strict, and because of Numeric/String mis-casting, I
  603. // want the "includes" to be "fuzzy".
  604. el.checked = value.some(val => checkedAttrLooseCompare(val, el.value));
  605. } else {
  606. el.checked = !!value;
  607. }
  608. }
  609. } else if (el.tagName === 'SELECT') {
  610. updateSelect(el, value);
  611. } else {
  612. if (el.value === value) return;
  613. el.value = value;
  614. }
  615. } else if (attrName === 'class') {
  616. if (Array.isArray(value)) {
  617. const originalClasses = el.__x_original_classes || [];
  618. el.setAttribute('class', arrayUnique(originalClasses.concat(value)).join(' '));
  619. } else if (typeof value === 'object') {
  620. // Sorting the keys / class names by their boolean value will ensure that
  621. // anything that evaluates to `false` and needs to remove classes is run first.
  622. const keysSortedByBooleanValue = Object.keys(value).sort((a, b) => value[a] - value[b]);
  623. keysSortedByBooleanValue.forEach(classNames => {
  624. if (value[classNames]) {
  625. convertClassStringToArray(classNames).forEach(className => el.classList.add(className));
  626. } else {
  627. convertClassStringToArray(classNames).forEach(className => el.classList.remove(className));
  628. }
  629. });
  630. } else {
  631. const originalClasses = el.__x_original_classes || [];
  632. const newClasses = value ? convertClassStringToArray(value) : [];
  633. el.setAttribute('class', arrayUnique(originalClasses.concat(newClasses)).join(' '));
  634. }
  635. } else {
  636. attrName = modifiers.includes('camel') ? camelCase(attrName) : attrName; // If an attribute's bound value is null, undefined or false, remove the attribute
  637. if ([null, undefined, false].includes(value)) {
  638. el.removeAttribute(attrName);
  639. } else {
  640. isBooleanAttr(attrName) ? setIfChanged(el, attrName, attrName) : setIfChanged(el, attrName, value);
  641. }
  642. }
  643. }
  644. function setIfChanged(el, attrName, value) {
  645. if (el.getAttribute(attrName) != value) {
  646. el.setAttribute(attrName, value);
  647. }
  648. }
  649. function updateSelect(el, value) {
  650. const arrayWrappedValue = [].concat(value).map(value => {
  651. return value + '';
  652. });
  653. Array.from(el.options).forEach(option => {
  654. option.selected = arrayWrappedValue.includes(option.value);
  655. });
  656. }
  657. function handleTextDirective(el, output, expression) {
  658. // If nested model key is undefined, set the default value to empty string.
  659. if (output === undefined && String(expression).match(/\./)) {
  660. output = '';
  661. }
  662. el.textContent = output;
  663. }
  664. function handleHtmlDirective(component, el, expression, extraVars) {
  665. el.innerHTML = component.evaluateReturnExpression(el, expression, extraVars);
  666. }
  667. function handleShowDirective(component, el, value, modifiers, initialUpdate = false) {
  668. const hide = () => {
  669. el.style.display = 'none';
  670. el.__x_is_shown = false;
  671. };
  672. const show = () => {
  673. if (el.style.length === 1 && el.style.display === 'none') {
  674. el.removeAttribute('style');
  675. } else {
  676. el.style.removeProperty('display');
  677. }
  678. el.__x_is_shown = true;
  679. };
  680. if (initialUpdate === true) {
  681. if (value) {
  682. show();
  683. } else {
  684. hide();
  685. }
  686. return;
  687. }
  688. const handle = (resolve, reject) => {
  689. if (value) {
  690. if (el.style.display === 'none' || el.__x_transition) {
  691. transitionIn(el, () => {
  692. show();
  693. }, reject, component);
  694. }
  695. resolve(() => {});
  696. } else {
  697. if (el.style.display !== 'none') {
  698. transitionOut(el, () => {
  699. resolve(() => {
  700. hide();
  701. });
  702. }, reject, component);
  703. } else {
  704. resolve(() => {});
  705. }
  706. }
  707. }; // The working of x-show is a bit complex because we need to
  708. // wait for any child transitions to finish before hiding
  709. // some element. Also, this has to be done recursively.
  710. // If x-show.immediate, foregoe the waiting.
  711. if (modifiers.includes('immediate')) {
  712. handle(finish => finish(), () => {});
  713. return;
  714. } // x-show is encountered during a DOM tree walk. If an element
  715. // we encounter is NOT a child of another x-show element we
  716. // can execute the previous x-show stack (if one exists).
  717. if (component.showDirectiveLastElement && !component.showDirectiveLastElement.contains(el)) {
  718. component.executeAndClearRemainingShowDirectiveStack();
  719. }
  720. component.showDirectiveStack.push(handle);
  721. component.showDirectiveLastElement = el;
  722. }
  723. function handleIfDirective(component, el, expressionResult, initialUpdate, extraVars) {
  724. warnIfMalformedTemplate(el, 'x-if');
  725. const elementHasAlreadyBeenAdded = el.nextElementSibling && el.nextElementSibling.__x_inserted_me === true;
  726. if (expressionResult && (!elementHasAlreadyBeenAdded || el.__x_transition)) {
  727. const clone = document.importNode(el.content, true);
  728. el.parentElement.insertBefore(clone, el.nextElementSibling);
  729. transitionIn(el.nextElementSibling, () => {}, () => {}, component, initialUpdate);
  730. component.initializeElements(el.nextElementSibling, extraVars);
  731. el.nextElementSibling.__x_inserted_me = true;
  732. } else if (!expressionResult && elementHasAlreadyBeenAdded) {
  733. transitionOut(el.nextElementSibling, () => {
  734. el.nextElementSibling.remove();
  735. }, () => {}, component, initialUpdate);
  736. }
  737. }
  738. function registerListener(component, el, event, modifiers, expression, extraVars = {}) {
  739. const options = {
  740. passive: modifiers.includes('passive')
  741. };
  742. if (modifiers.includes('camel')) {
  743. event = camelCase(event);
  744. }
  745. const node_add_count = el.__x_node_add_count;
  746. let handler, listenerTarget;
  747. if (modifiers.includes('away')) {
  748. listenerTarget = document;
  749. handler = e => {
  750. // Don't do anything if the click came from the element or within it.
  751. if (el.contains(e.target)) return; // Don't do anything if this element isn't currently visible.
  752. if (el.offsetWidth < 1 && el.offsetHeight < 1) return; // Now that we are sure the element is visible, AND the click
  753. // is from outside it, let's run the expression.
  754. runListenerHandler(component, expression, e, extraVars);
  755. if (modifiers.includes('once')) {
  756. document.removeEventListener(event, handler, options);
  757. }
  758. };
  759. } else {
  760. listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
  761. handler = e => {
  762. // Remove this global event handler if the element that declared it
  763. // has been removed. It's now stale.
  764. if (listenerTarget === window || listenerTarget === document) {
  765. if (!document.body.contains(el)) {
  766. listenerTarget.removeEventListener(event, handler, options);
  767. return;
  768. }
  769. }
  770. if (el.__x_node_add_count !== node_add_count) {
  771. listenerTarget.removeEventListener(event, handler, options);
  772. return;
  773. }
  774. if (isKeyEvent(event)) {
  775. if (isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers)) {
  776. return;
  777. }
  778. }
  779. if (modifiers.includes('prevent')) e.preventDefault();
  780. if (modifiers.includes('stop')) e.stopPropagation(); // If the .self modifier isn't present, or if it is present and
  781. // the target element matches the element we are registering the
  782. // event on, run the handler
  783. if (!modifiers.includes('self') || e.target === el) {
  784. const returnValue = runListenerHandler(component, expression, e, extraVars);
  785. returnValue.then(value => {
  786. if (value === false) {
  787. e.preventDefault();
  788. } else {
  789. if (modifiers.includes('once')) {
  790. listenerTarget.removeEventListener(event, handler, options);
  791. }
  792. }
  793. });
  794. }
  795. };
  796. }
  797. if (modifiers.includes('debounce')) {
  798. let nextModifier = modifiers[modifiers.indexOf('debounce') + 1] || 'invalid-wait';
  799. let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250;
  800. handler = debounce(handler, wait);
  801. }
  802. listenerTarget.addEventListener(event, handler, options);
  803. }
  804. function runListenerHandler(component, expression, e, extraVars) {
  805. return component.evaluateCommandExpression(e.target, expression, () => {
  806. return _objectSpread2(_objectSpread2({}, extraVars()), {}, {
  807. '$event': e
  808. });
  809. });
  810. }
  811. function isKeyEvent(event) {
  812. return ['keydown', 'keyup'].includes(event);
  813. }
  814. function isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers) {
  815. let keyModifiers = modifiers.filter(i => {
  816. return !['window', 'document', 'prevent', 'stop'].includes(i);
  817. });
  818. if (keyModifiers.includes('debounce')) {
  819. let debounceIndex = keyModifiers.indexOf('debounce');
  820. keyModifiers.splice(debounceIndex, isNumeric((keyModifiers[debounceIndex + 1] || 'invalid-wait').split('ms')[0]) ? 2 : 1);
  821. } // If no modifier is specified, we'll call it a press.
  822. if (keyModifiers.length === 0) return false; // If one is passed, AND it matches the key pressed, we'll call it a press.
  823. if (keyModifiers.length === 1 && keyModifiers[0] === keyToModifier(e.key)) return false; // The user is listening for key combinations.
  824. const systemKeyModifiers = ['ctrl', 'shift', 'alt', 'meta', 'cmd', 'super'];
  825. const selectedSystemKeyModifiers = systemKeyModifiers.filter(modifier => keyModifiers.includes(modifier));
  826. keyModifiers = keyModifiers.filter(i => !selectedSystemKeyModifiers.includes(i));
  827. if (selectedSystemKeyModifiers.length > 0) {
  828. const activelyPressedKeyModifiers = selectedSystemKeyModifiers.filter(modifier => {
  829. // Alias "cmd" and "super" to "meta"
  830. if (modifier === 'cmd' || modifier === 'super') modifier = 'meta';
  831. return e[`${modifier}Key`];
  832. }); // If all the modifiers selected are pressed, ...
  833. if (activelyPressedKeyModifiers.length === selectedSystemKeyModifiers.length) {
  834. // AND the remaining key is pressed as well. It's a press.
  835. if (keyModifiers[0] === keyToModifier(e.key)) return false;
  836. }
  837. } // We'll call it NOT a valid keypress.
  838. return true;
  839. }
  840. function keyToModifier(key) {
  841. switch (key) {
  842. case '/':
  843. return 'slash';
  844. case ' ':
  845. case 'Spacebar':
  846. return 'space';
  847. default:
  848. return key && kebabCase(key);
  849. }
  850. }
  851. function registerModelListener(component, el, modifiers, expression, extraVars) {
  852. // If the element we are binding to is a select, a radio, or checkbox
  853. // we'll listen for the change event instead of the "input" event.
  854. var event = el.tagName.toLowerCase() === 'select' || ['checkbox', 'radio'].includes(el.type) || modifiers.includes('lazy') ? 'change' : 'input';
  855. const listenerExpression = `${expression} = rightSideOfExpression($event, ${expression})`;
  856. registerListener(component, el, event, modifiers, listenerExpression, () => {
  857. return _objectSpread2(_objectSpread2({}, extraVars()), {}, {
  858. rightSideOfExpression: generateModelAssignmentFunction(el, modifiers, expression)
  859. });
  860. });
  861. }
  862. function generateModelAssignmentFunction(el, modifiers, expression) {
  863. if (el.type === 'radio') {
  864. // Radio buttons only work properly when they share a name attribute.
  865. // People might assume we take care of that for them, because
  866. // they already set a shared "x-model" attribute.
  867. if (!el.hasAttribute('name')) el.setAttribute('name', expression);
  868. }
  869. return (event, currentValue) => {
  870. // Check for event.detail due to an issue where IE11 handles other events as a CustomEvent.
  871. if (event instanceof CustomEvent && event.detail) {
  872. return event.detail;
  873. } else if (el.type === 'checkbox') {
  874. // If the data we are binding to is an array, toggle its value inside the array.
  875. if (Array.isArray(currentValue)) {
  876. const newValue = modifiers.includes('number') ? safeParseNumber(event.target.value) : event.target.value;
  877. return event.target.checked ? currentValue.concat([newValue]) : currentValue.filter(el => !checkedAttrLooseCompare(el, newValue));
  878. } else {
  879. return event.target.checked;
  880. }
  881. } else if (el.tagName.toLowerCase() === 'select' && el.multiple) {
  882. return modifiers.includes('number') ? Array.from(event.target.selectedOptions).map(option => {
  883. const rawValue = option.value || option.text;
  884. return safeParseNumber(rawValue);
  885. }) : Array.from(event.target.selectedOptions).map(option => {
  886. return option.value || option.text;
  887. });
  888. } else {
  889. const rawValue = event.target.value;
  890. return modifiers.includes('number') ? safeParseNumber(rawValue) : modifiers.includes('trim') ? rawValue.trim() : rawValue;
  891. }
  892. };
  893. }
  894. function safeParseNumber(rawValue) {
  895. const number = rawValue ? parseFloat(rawValue) : null;
  896. return isNumeric(number) ? number : rawValue;
  897. }
  898. /**
  899. * Copyright (C) 2017 salesforce.com, inc.
  900. */
  901. const { isArray } = Array;
  902. const { getPrototypeOf, create: ObjectCreate, defineProperty: ObjectDefineProperty, defineProperties: ObjectDefineProperties, isExtensible, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols, preventExtensions, hasOwnProperty, } = Object;
  903. const { push: ArrayPush, concat: ArrayConcat, map: ArrayMap, } = Array.prototype;
  904. function isUndefined(obj) {
  905. return obj === undefined;
  906. }
  907. function isFunction(obj) {
  908. return typeof obj === 'function';
  909. }
  910. function isObject(obj) {
  911. return typeof obj === 'object';
  912. }
  913. const proxyToValueMap = new WeakMap();
  914. function registerProxy(proxy, value) {
  915. proxyToValueMap.set(proxy, value);
  916. }
  917. const unwrap$1 = (replicaOrAny) => proxyToValueMap.get(replicaOrAny) || replicaOrAny;
  918. function wrapValue(membrane, value) {
  919. return membrane.valueIsObservable(value) ? membrane.getProxy(value) : value;
  920. }
  921. /**
  922. * Unwrap property descriptors will set value on original descriptor
  923. * We only need to unwrap if value is specified
  924. * @param descriptor external descrpitor provided to define new property on original value
  925. */
  926. function unwrapDescriptor(descriptor) {
  927. if (hasOwnProperty.call(descriptor, 'value')) {
  928. descriptor.value = unwrap$1(descriptor.value);
  929. }
  930. return descriptor;
  931. }
  932. function lockShadowTarget(membrane, shadowTarget, originalTarget) {
  933. const targetKeys = ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
  934. targetKeys.forEach((key) => {
  935. let descriptor = getOwnPropertyDescriptor(originalTarget, key);
  936. // We do not need to wrap the descriptor if configurable
  937. // Because we can deal with wrapping it when user goes through
  938. // Get own property descriptor. There is also a chance that this descriptor
  939. // could change sometime in the future, so we can defer wrapping
  940. // until we need to
  941. if (!descriptor.configurable) {
  942. descriptor = wrapDescriptor(membrane, descriptor, wrapValue);
  943. }
  944. ObjectDefineProperty(shadowTarget, key, descriptor);
  945. });
  946. preventExtensions(shadowTarget);
  947. }
  948. class ReactiveProxyHandler {
  949. constructor(membrane, value) {
  950. this.originalTarget = value;
  951. this.membrane = membrane;
  952. }
  953. get(shadowTarget, key) {
  954. const { originalTarget, membrane } = this;
  955. const value = originalTarget[key];
  956. const { valueObserved } = membrane;
  957. valueObserved(originalTarget, key);
  958. return membrane.getProxy(value);
  959. }
  960. set(shadowTarget, key, value) {
  961. const { originalTarget, membrane: { valueMutated } } = this;
  962. const oldValue = originalTarget[key];
  963. if (oldValue !== value) {
  964. originalTarget[key] = value;
  965. valueMutated(originalTarget, key);
  966. }
  967. else if (key === 'length' && isArray(originalTarget)) {
  968. // fix for issue #236: push will add the new index, and by the time length
  969. // is updated, the internal length is already equal to the new length value
  970. // therefore, the oldValue is equal to the value. This is the forking logic
  971. // to support this use case.
  972. valueMutated(originalTarget, key);
  973. }
  974. return true;
  975. }
  976. deleteProperty(shadowTarget, key) {
  977. const { originalTarget, membrane: { valueMutated } } = this;
  978. delete originalTarget[key];
  979. valueMutated(originalTarget, key);
  980. return true;
  981. }
  982. apply(shadowTarget, thisArg, argArray) {
  983. /* No op */
  984. }
  985. construct(target, argArray, newTarget) {
  986. /* No op */
  987. }
  988. has(shadowTarget, key) {
  989. const { originalTarget, membrane: { valueObserved } } = this;
  990. valueObserved(originalTarget, key);
  991. return key in originalTarget;
  992. }
  993. ownKeys(shadowTarget) {
  994. const { originalTarget } = this;
  995. return ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
  996. }
  997. isExtensible(shadowTarget) {
  998. const shadowIsExtensible = isExtensible(shadowTarget);
  999. if (!shadowIsExtensible) {
  1000. return shadowIsExtensible;
  1001. }
  1002. const { originalTarget, membrane } = this;
  1003. const targetIsExtensible = isExtensible(originalTarget);
  1004. if (!targetIsExtensible) {
  1005. lockShadowTarget(membrane, shadowTarget, originalTarget);
  1006. }
  1007. return targetIsExtensible;
  1008. }
  1009. setPrototypeOf(shadowTarget, prototype) {
  1010. }
  1011. getPrototypeOf(shadowTarget) {
  1012. const { originalTarget } = this;
  1013. return getPrototypeOf(originalTarget);
  1014. }
  1015. getOwnPropertyDescriptor(shadowTarget, key) {
  1016. const { originalTarget, membrane } = this;
  1017. const { valueObserved } = this.membrane;
  1018. // keys looked up via hasOwnProperty need to be reactive
  1019. valueObserved(originalTarget, key);
  1020. let desc = getOwnPropertyDescriptor(originalTarget, key);
  1021. if (isUndefined(desc)) {
  1022. return desc;
  1023. }
  1024. const shadowDescriptor = getOwnPropertyDescriptor(shadowTarget, key);
  1025. if (!isUndefined(shadowDescriptor)) {
  1026. return shadowDescriptor;
  1027. }
  1028. // Note: by accessing the descriptor, the key is marked as observed
  1029. // but access to the value, setter or getter (if available) cannot observe
  1030. // mutations, just like regular methods, in which case we just do nothing.
  1031. desc = wrapDescriptor(membrane, desc, wrapValue);
  1032. if (!desc.configurable) {
  1033. // If descriptor from original target is not configurable,
  1034. // We must copy the wrapped descriptor over to the shadow target.
  1035. // Otherwise, proxy will throw an invariant error.
  1036. // This is our last chance to lock the value.
  1037. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Invariants
  1038. ObjectDefineProperty(shadowTarget, key, desc);
  1039. }
  1040. return desc;
  1041. }
  1042. preventExtensions(shadowTarget) {
  1043. const { originalTarget, membrane } = this;
  1044. lockShadowTarget(membrane, shadowTarget, originalTarget);
  1045. preventExtensions(originalTarget);
  1046. return true;
  1047. }
  1048. defineProperty(shadowTarget, key, descriptor) {
  1049. const { originalTarget, membrane } = this;
  1050. const { valueMutated } = membrane;
  1051. const { configurable } = descriptor;
  1052. // We have to check for value in descriptor
  1053. // because Object.freeze(proxy) calls this method
  1054. // with only { configurable: false, writeable: false }
  1055. // Additionally, method will only be called with writeable:false
  1056. // if the descriptor has a value, as opposed to getter/setter
  1057. // So we can just check if writable is present and then see if
  1058. // value is present. This eliminates getter and setter descriptors
  1059. if (hasOwnProperty.call(descriptor, 'writable') && !hasOwnProperty.call(descriptor, 'value')) {
  1060. const originalDescriptor = getOwnPropertyDescriptor(originalTarget, key);
  1061. descriptor.value = originalDescriptor.value;
  1062. }
  1063. ObjectDefineProperty(originalTarget, key, unwrapDescriptor(descriptor));
  1064. if (configurable === false) {
  1065. ObjectDefineProperty(shadowTarget, key, wrapDescriptor(membrane, descriptor, wrapValue));
  1066. }
  1067. valueMutated(originalTarget, key);
  1068. return true;
  1069. }
  1070. }
  1071. function wrapReadOnlyValue(membrane, value) {
  1072. return membrane.valueIsObservable(value) ? membrane.getReadOnlyProxy(value) : value;
  1073. }
  1074. class ReadOnlyHandler {
  1075. constructor(membrane, value) {
  1076. this.originalTarget = value;
  1077. this.membrane = membrane;
  1078. }
  1079. get(shadowTarget, key) {
  1080. const { membrane, originalTarget } = this;
  1081. const value = originalTarget[key];
  1082. const { valueObserved } = membrane;
  1083. valueObserved(originalTarget, key);
  1084. return membrane.getReadOnlyProxy(value);
  1085. }
  1086. set(shadowTarget, key, value) {
  1087. return false;
  1088. }
  1089. deleteProperty(shadowTarget, key) {
  1090. return false;
  1091. }
  1092. apply(shadowTarget, thisArg, argArray) {
  1093. /* No op */
  1094. }
  1095. construct(target, argArray, newTarget) {
  1096. /* No op */
  1097. }
  1098. has(shadowTarget, key) {
  1099. const { originalTarget, membrane: { valueObserved } } = this;
  1100. valueObserved(originalTarget, key);
  1101. return key in originalTarget;
  1102. }
  1103. ownKeys(shadowTarget) {
  1104. const { originalTarget } = this;
  1105. return ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
  1106. }
  1107. setPrototypeOf(shadowTarget, prototype) {
  1108. }
  1109. getOwnPropertyDescriptor(shadowTarget, key) {
  1110. const { originalTarget, membrane } = this;
  1111. const { valueObserved } = membrane;
  1112. // keys looked up via hasOwnProperty need to be reactive
  1113. valueObserved(originalTarget, key);
  1114. let desc = getOwnPropertyDescriptor(originalTarget, key);
  1115. if (isUndefined(desc)) {
  1116. return desc;
  1117. }
  1118. const shadowDescriptor = getOwnPropertyDescriptor(shadowTarget, key);
  1119. if (!isUndefined(shadowDescriptor)) {
  1120. return shadowDescriptor;
  1121. }
  1122. // Note: by accessing the descriptor, the key is marked as observed
  1123. // but access to the value or getter (if available) cannot be observed,
  1124. // just like regular methods, in which case we just do nothing.
  1125. desc = wrapDescriptor(membrane, desc, wrapReadOnlyValue);
  1126. if (hasOwnProperty.call(desc, 'set')) {
  1127. desc.set = undefined; // readOnly membrane does not allow setters
  1128. }
  1129. if (!desc.configurable) {
  1130. // If descriptor from original target is not configurable,
  1131. // We must copy the wrapped descriptor over to the shadow target.
  1132. // Otherwise, proxy will throw an invariant error.
  1133. // This is our last chance to lock the value.
  1134. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Invariants
  1135. ObjectDefineProperty(shadowTarget, key, desc);
  1136. }
  1137. return desc;
  1138. }
  1139. preventExtensions(shadowTarget) {
  1140. return false;
  1141. }
  1142. defineProperty(shadowTarget, key, descriptor) {
  1143. return false;
  1144. }
  1145. }
  1146. function createShadowTarget(value) {
  1147. let shadowTarget = undefined;
  1148. if (isArray(value)) {
  1149. shadowTarget = [];
  1150. }
  1151. else if (isObject(value)) {
  1152. shadowTarget = {};
  1153. }
  1154. return shadowTarget;
  1155. }
  1156. const ObjectDotPrototype = Object.prototype;
  1157. function defaultValueIsObservable(value) {
  1158. // intentionally checking for null
  1159. if (value === null) {
  1160. return false;
  1161. }
  1162. // treat all non-object types, including undefined, as non-observable values
  1163. if (typeof value !== 'object') {
  1164. return false;
  1165. }
  1166. if (isArray(value)) {
  1167. return true;
  1168. }
  1169. const proto = getPrototypeOf(value);
  1170. return (proto === ObjectDotPrototype || proto === null || getPrototypeOf(proto) === null);
  1171. }
  1172. const defaultValueObserved = (obj, key) => {
  1173. /* do nothing */
  1174. };
  1175. const defaultValueMutated = (obj, key) => {
  1176. /* do nothing */
  1177. };
  1178. const defaultValueDistortion = (value) => value;
  1179. function wrapDescriptor(membrane, descriptor, getValue) {
  1180. const { set, get } = descriptor;
  1181. if (hasOwnProperty.call(descriptor, 'value')) {
  1182. descriptor.value = getValue(membrane, descriptor.value);
  1183. }
  1184. else {
  1185. if (!isUndefined(get)) {
  1186. descriptor.get = function () {
  1187. // invoking the original getter with the original target
  1188. return getValue(membrane, get.call(unwrap$1(this)));
  1189. };
  1190. }
  1191. if (!isUndefined(set)) {
  1192. descriptor.set = function (value) {
  1193. // At this point we don't have a clear indication of whether
  1194. // or not a valid mutation will occur, we don't have the key,
  1195. // and we are not sure why and how they are invoking this setter.
  1196. // Nevertheless we preserve the original semantics by invoking the
  1197. // original setter with the original target and the unwrapped value
  1198. set.call(unwrap$1(this), membrane.unwrapProxy(value));
  1199. };
  1200. }
  1201. }
  1202. return descriptor;
  1203. }
  1204. class ReactiveMembrane {
  1205. constructor(options) {
  1206. this.valueDistortion = defaultValueDistortion;
  1207. this.valueMutated = defaultValueMutated;
  1208. this.valueObserved = defaultValueObserved;
  1209. this.valueIsObservable = defaultValueIsObservable;
  1210. this.objectGraph = new WeakMap();
  1211. if (!isUndefined(options)) {
  1212. const { valueDistortion, valueMutated, valueObserved, valueIsObservable } = options;
  1213. this.valueDistortion = isFunction(valueDistortion) ? valueDistortion : defaultValueDistortion;
  1214. this.valueMutated = isFunction(valueMutated) ? valueMutated : defaultValueMutated;
  1215. this.valueObserved = isFunction(valueObserved) ? valueObserved : defaultValueObserved;
  1216. this.valueIsObservable = isFunction(valueIsObservable) ? valueIsObservable : defaultValueIsObservable;
  1217. }
  1218. }
  1219. getProxy(value) {
  1220. const unwrappedValue = unwrap$1(value);
  1221. const distorted = this.valueDistortion(unwrappedValue);
  1222. if (this.valueIsObservable(distorted)) {
  1223. const o = this.getReactiveState(unwrappedValue, distorted);
  1224. // when trying to extract the writable version of a readonly
  1225. // we return the readonly.
  1226. return o.readOnly === value ? value : o.reactive;
  1227. }
  1228. return distorted;
  1229. }
  1230. getReadOnlyProxy(value) {
  1231. value = unwrap$1(value);
  1232. const distorted = this.valueDistortion(value);
  1233. if (this.valueIsObservable(distorted)) {
  1234. return this.getReactiveState(value, distorted).readOnly;
  1235. }
  1236. return distorted;
  1237. }
  1238. unwrapProxy(p) {
  1239. return unwrap$1(p);
  1240. }
  1241. getReactiveState(value, distortedValue) {
  1242. const { objectGraph, } = this;
  1243. let reactiveState = objectGraph.get(distortedValue);
  1244. if (reactiveState) {
  1245. return reactiveState;
  1246. }
  1247. const membrane = this;
  1248. reactiveState = {
  1249. get reactive() {
  1250. const reactiveHandler = new ReactiveProxyHandler(membrane, distortedValue);
  1251. // caching the reactive proxy after the first time it is accessed
  1252. const proxy = new Proxy(createShadowTarget(distortedValue), reactiveHandler);
  1253. registerProxy(proxy, value);
  1254. ObjectDefineProperty(this, 'reactive', { value: proxy });
  1255. return proxy;
  1256. },
  1257. get readOnly() {
  1258. const readOnlyHandler = new ReadOnlyHandler(membrane, distortedValue);
  1259. // caching the readOnly proxy after the first time it is accessed
  1260. const proxy = new Proxy(createShadowTarget(distortedValue), readOnlyHandler);
  1261. registerProxy(proxy, value);
  1262. ObjectDefineProperty(this, 'readOnly', { value: proxy });
  1263. return proxy;
  1264. }
  1265. };
  1266. objectGraph.set(distortedValue, reactiveState);
  1267. return reactiveState;
  1268. }
  1269. }
  1270. /** version: 0.26.0 */
  1271. function wrap(data, mutationCallback) {
  1272. let membrane = new ReactiveMembrane({
  1273. valueMutated(target, key) {
  1274. mutationCallback(target, key);
  1275. }
  1276. });
  1277. return {
  1278. data: membrane.getProxy(data),
  1279. membrane: membrane
  1280. };
  1281. }
  1282. function unwrap(membrane, observable) {
  1283. let unwrappedData = membrane.unwrapProxy(observable);
  1284. let copy = {};
  1285. Object.keys(unwrappedData).forEach(key => {
  1286. if (['$el', '$refs', '$nextTick', '$watch'].includes(key)) return;
  1287. copy[key] = unwrappedData[key];
  1288. });
  1289. return copy;
  1290. }
  1291. class Component {
  1292. constructor(el, componentForClone = null) {
  1293. this.$el = el;
  1294. const dataAttr = this.$el.getAttribute('x-data');
  1295. const dataExpression = dataAttr === '' ? '{}' : dataAttr;
  1296. let dataExtras = {
  1297. $el: this.$el
  1298. };
  1299. let canonicalComponentElementReference = componentForClone ? componentForClone.$el : this.$el;
  1300. Object.entries(Alpine.magicProperties).forEach(([name, callback]) => {
  1301. Object.defineProperty(dataExtras, `$${name}`, {
  1302. get: function get() {
  1303. return callback(canonicalComponentElementReference);
  1304. }
  1305. });
  1306. });
  1307. this.unobservedData = componentForClone ? componentForClone.getUnobservedData() : saferEval(el, dataExpression, dataExtras);
  1308. // Construct a Proxy-based observable. This will be used to handle reactivity.
  1309. let {
  1310. membrane,
  1311. data
  1312. } = this.wrapDataInObservable(this.unobservedData);
  1313. this.$data = data;
  1314. this.membrane = membrane; // After making user-supplied data methods reactive, we can now add
  1315. // our magic properties to the original data for access.
  1316. this.unobservedData.$el = this.$el;
  1317. this.unobservedData.$refs = this.getRefsProxy();
  1318. this.nextTickStack = [];
  1319. this.unobservedData.$nextTick = callback => {
  1320. this.nextTickStack.push(callback);
  1321. };
  1322. this.watchers = {};
  1323. this.unobservedData.$watch = (property, callback) => {
  1324. if (!this.watchers[property]) this.watchers[property] = [];
  1325. this.watchers[property].push(callback);
  1326. };
  1327. /* MODERN-ONLY:START */
  1328. // We remove this piece of code from the legacy build.
  1329. // In IE11, we have already defined our helpers at this point.
  1330. // Register custom magic properties.
  1331. Object.entries(Alpine.magicProperties).forEach(([name, callback]) => {
  1332. Object.defineProperty(this.unobservedData, `$${name}`, {
  1333. get: function get() {
  1334. return callback(canonicalComponentElementReference, this.$el);
  1335. }
  1336. });
  1337. });
  1338. /* MODERN-ONLY:END */
  1339. this.showDirectiveStack = [];
  1340. this.showDirectiveLastElement;
  1341. componentForClone || Alpine.onBeforeComponentInitializeds.forEach(callback => callback(this));
  1342. const initExpression = this.$el.getAttribute('x-init');
  1343. var initReturnedCallback; // If x-init is present AND we aren't cloning (skip x-init on clone)
  1344. if (initExpression && !componentForClone) {
  1345. // We want to allow data manipulation, but not trigger DOM updates just yet.
  1346. // We haven't even initialized the elements with their Alpine bindings. I mean c'mon.
  1347. this.pauseReactivity = true;
  1348. initReturnedCallback = this.evaluateReturnExpression(this.$el, initExpression);
  1349. this.pauseReactivity = false;
  1350. } // Register all our listeners and set all our attribute bindings.
  1351. // If we're cloning a component, the third parameter ensures no duplicate
  1352. // event listeners are registered (the mutation observer will take care of them)
  1353. this.initializeElements(this.$el, () => {}, componentForClone); // Use mutation observer to detect new elements being added within this component at run-time.
  1354. // Alpine's just so darn flexible amirite?
  1355. this.listenForNewElementsToInitialize();
  1356. if (typeof initReturnedCallback === 'function') {
  1357. // Run the callback returned from the "x-init" hook to allow the user to do stuff after
  1358. // Alpine's got it's grubby little paws all over everything.
  1359. initReturnedCallback.call(this.$data);
  1360. }
  1361. componentForClone || setTimeout(() => {
  1362. Alpine.onComponentInitializeds.forEach(callback => callback(this));
  1363. }, 0);
  1364. }
  1365. getUnobservedData() {
  1366. return unwrap(this.membrane, this.$data);
  1367. }
  1368. wrapDataInObservable(data) {
  1369. var self = this;
  1370. let updateDom = debounce(function () {
  1371. self.updateElements(self.$el);
  1372. }, 0);
  1373. return wrap(data, (target, key) => {
  1374. if (self.watchers[key]) {
  1375. // If there's a watcher for this specific key, run it.
  1376. self.watchers[key].forEach(callback => callback(target[key]));
  1377. } else if (Array.isArray(target)) {
  1378. // Arrays are special cases, if any of the items change, we consider the array as mutated.
  1379. Object.keys(self.watchers).forEach(fullDotNotationKey => {
  1380. let dotNotationParts = fullDotNotationKey.split('.'); // Ignore length mutations since they would result in duplicate calls.
  1381. // For example, when calling push, we would get a mutation for the item's key
  1382. // and a second mutation for the length property.
  1383. if (key === 'length') return;
  1384. dotNotationParts.reduce((comparisonData, part) => {
  1385. if (Object.is(target, comparisonData[part])) {
  1386. self.watchers[fullDotNotationKey].forEach(callback => callback(target));
  1387. }
  1388. return comparisonData[part];
  1389. }, self.unobservedData);
  1390. });
  1391. } else {
  1392. // Let's walk through the watchers with "dot-notation" (foo.bar) and see
  1393. // if this mutation fits any of them.
  1394. Object.keys(self.watchers).filter(i => i.includes('.')).forEach(fullDotNotationKey => {
  1395. let dotNotationParts = fullDotNotationKey.split('.'); // If this dot-notation watcher's last "part" doesn't match the current
  1396. // key, then skip it early for performance reasons.
  1397. if (key !== dotNotationParts[dotNotationParts.length - 1]) return; // Now, walk through the dot-notation "parts" recursively to find
  1398. // a match, and call the watcher if one's found.
  1399. dotNotationParts.reduce((comparisonData, part) => {
  1400. if (Object.is(target, comparisonData)) {
  1401. // Run the watchers.
  1402. self.watchers[fullDotNotationKey].forEach(callback => callback(target[key]));
  1403. }
  1404. return comparisonData[part];
  1405. }, self.unobservedData);
  1406. });
  1407. } // Don't react to data changes for cases like the `x-created` hook.
  1408. if (self.pauseReactivity) return;
  1409. updateDom();
  1410. });
  1411. }
  1412. walkAndSkipNestedComponents(el, callback, initializeComponentCallback = () => {}) {
  1413. walk(el, el => {
  1414. // We've hit a component.
  1415. if (el.hasAttribute('x-data')) {
  1416. // If it's not the current one.
  1417. if (!el.isSameNode(this.$el)) {
  1418. // Initialize it if it's not.
  1419. if (!el.__x) initializeComponentCallback(el); // Now we'll let that sub-component deal with itself.
  1420. return false;
  1421. }
  1422. }
  1423. return callback(el);
  1424. });
  1425. }
  1426. initializeElements(rootEl, extraVars = () => {}, componentForClone = false) {
  1427. this.walkAndSkipNestedComponents(rootEl, el => {
  1428. // Don't touch spawns from for loop
  1429. if (el.__x_for_key !== undefined) return false; // Don't touch spawns from if directives
  1430. if (el.__x_inserted_me !== undefined) return false;
  1431. this.initializeElement(el, extraVars, componentForClone ? false : true);
  1432. }, el => {
  1433. if (!componentForClone) el.__x = new Component(el);
  1434. });
  1435. this.executeAndClearRemainingShowDirectiveStack();
  1436. this.executeAndClearNextTickStack(rootEl);
  1437. }
  1438. initializeElement(el, extraVars, shouldRegisterListeners = true) {
  1439. // To support class attribute merging, we have to know what the element's
  1440. // original class attribute looked like for reference.
  1441. if (el.hasAttribute('class') && getXAttrs(el, this).length > 0) {
  1442. el.__x_original_classes = convertClassStringToArray(el.getAttribute('class'));
  1443. }
  1444. shouldRegisterListeners && this.registerListeners(el, extraVars);
  1445. this.resolveBoundAttributes(el, true, extraVars);
  1446. }
  1447. updateElements(rootEl, extraVars = () => {}) {
  1448. this.walkAndSkipNestedComponents(rootEl, el => {
  1449. // Don't touch spawns from for loop (and check if the root is actually a for loop in a parent, don't skip it.)
  1450. if (el.__x_for_key !== undefined && !el.isSameNode(this.$el)) return false;
  1451. this.updateElement(el, extraVars);
  1452. }, el => {
  1453. el.__x = new Component(el);
  1454. });
  1455. this.executeAndClearRemainingShowDirectiveStack();
  1456. this.executeAndClearNextTickStack(rootEl);
  1457. }
  1458. executeAndClearNextTickStack(el) {
  1459. // Skip spawns from alpine directives
  1460. if (el === this.$el && this.nextTickStack.length > 0) {
  1461. // We run the tick stack after the next frame to allow any
  1462. // running transitions to pass the initial show stage.
  1463. requestAnimationFrame(() => {
  1464. while (this.nextTickStack.length > 0) {
  1465. this.nextTickStack.shift()();
  1466. }
  1467. });
  1468. }
  1469. }
  1470. executeAndClearRemainingShowDirectiveStack() {
  1471. // The goal here is to start all the x-show transitions
  1472. // and build a nested promise chain so that elements
  1473. // only hide when the children are finished hiding.
  1474. this.showDirectiveStack.reverse().map(handler => {
  1475. return new Promise((resolve, reject) => {
  1476. handler(resolve, reject);
  1477. });
  1478. }).reduce((promiseChain, promise) => {
  1479. return promiseChain.then(() => {
  1480. return promise.then(finishElement => {
  1481. finishElement();
  1482. });
  1483. });
  1484. }, Promise.resolve(() => {})).catch(e => {
  1485. if (e !== TRANSITION_CANCELLED) throw e;
  1486. }); // We've processed the handler stack. let's clear it.
  1487. this.showDirectiveStack = [];
  1488. this.showDirectiveLastElement = undefined;
  1489. }
  1490. updateElement(el, extraVars) {
  1491. this.resolveBoundAttributes(el, false, extraVars);
  1492. }
  1493. registerListeners(el, extraVars) {
  1494. getXAttrs(el, this).forEach(({
  1495. type,
  1496. value,
  1497. modifiers,
  1498. expression
  1499. }) => {
  1500. switch (type) {
  1501. case 'on':
  1502. registerListener(this, el, value, modifiers, expression, extraVars);
  1503. break;
  1504. case 'model':
  1505. registerModelListener(this, el, modifiers, expression, extraVars);
  1506. break;
  1507. }
  1508. });
  1509. }
  1510. resolveBoundAttributes(el, initialUpdate = false, extraVars) {
  1511. let attrs = getXAttrs(el, this);
  1512. attrs.forEach(({
  1513. type,
  1514. value,
  1515. modifiers,
  1516. expression
  1517. }) => {
  1518. switch (type) {
  1519. case 'model':
  1520. handleAttributeBindingDirective(this, el, 'value', expression, extraVars, type, modifiers);
  1521. break;
  1522. case 'bind':
  1523. // The :key binding on an x-for is special, ignore it.
  1524. if (el.tagName.toLowerCase() === 'template' && value === 'key') return;
  1525. handleAttributeBindingDirective(this, el, value, expression, extraVars, type, modifiers);
  1526. break;
  1527. case 'text':
  1528. var output = this.evaluateReturnExpression(el, expression, extraVars);
  1529. handleTextDirective(el, output, expression);
  1530. break;
  1531. case 'html':
  1532. handleHtmlDirective(this, el, expression, extraVars);
  1533. break;
  1534. case 'show':
  1535. var output = this.evaluateReturnExpression(el, expression, extraVars);
  1536. handleShowDirective(this, el, output, modifiers, initialUpdate);
  1537. break;
  1538. case 'if':
  1539. // If this element also has x-for on it, don't process x-if.
  1540. // We will let the "x-for" directive handle the "if"ing.
  1541. if (attrs.some(i => i.type === 'for')) return;
  1542. var output = this.evaluateReturnExpression(el, expression, extraVars);
  1543. handleIfDirective(this, el, output, initialUpdate, extraVars);
  1544. break;
  1545. case 'for':
  1546. handleForDirective(this, el, expression, initialUpdate, extraVars);
  1547. break;
  1548. case 'cloak':
  1549. el.removeAttribute('x-cloak');
  1550. break;
  1551. }
  1552. });
  1553. }
  1554. evaluateReturnExpression(el, expression, extraVars = () => {}) {
  1555. return saferEval(el, expression, this.$data, _objectSpread2(_objectSpread2({}, extraVars()), {}, {
  1556. $dispatch: this.getDispatchFunction(el)
  1557. }));
  1558. }
  1559. evaluateCommandExpression(el, expression, extraVars = () => {}) {
  1560. return saferEvalNoReturn(el, expression, this.$data, _objectSpread2(_objectSpread2({}, extraVars()), {}, {
  1561. $dispatch: this.getDispatchFunction(el)
  1562. }));
  1563. }
  1564. getDispatchFunction(el) {
  1565. return (event, detail = {}) => {
  1566. el.dispatchEvent(new CustomEvent(event, {
  1567. detail,
  1568. bubbles: true
  1569. }));
  1570. };
  1571. }
  1572. listenForNewElementsToInitialize() {
  1573. const targetNode = this.$el;
  1574. const observerOptions = {
  1575. childList: true,
  1576. attributes: true,
  1577. subtree: true
  1578. };
  1579. const observer = new MutationObserver(mutations => {
  1580. for (let i = 0; i < mutations.length; i++) {
  1581. // Filter out mutations triggered from child components.
  1582. const closestParentComponent = mutations[i].target.closest('[x-data]');
  1583. if (!(closestParentComponent && closestParentComponent.isSameNode(this.$el))) continue;
  1584. if (mutations[i].type === 'attributes' && mutations[i].attributeName === 'x-data') {
  1585. const xAttr = mutations[i].target.getAttribute('x-data') || '{}';
  1586. const rawData = saferEval(this.$el, xAttr, {
  1587. $el: this.$el
  1588. });
  1589. Object.keys(rawData).forEach(key => {
  1590. if (this.$data[key] !== rawData[key]) {
  1591. this.$data[key] = rawData[key];
  1592. }
  1593. });
  1594. }
  1595. if (mutations[i].addedNodes.length > 0) {
  1596. mutations[i].addedNodes.forEach(node => {
  1597. if (node.nodeType !== 1 || node.__x_inserted_me) return;
  1598. if (node.matches('[x-data]') && !node.__x) {
  1599. node.__x = new Component(node);
  1600. return;
  1601. }
  1602. node.__x_node_add_count = (node.__x_node_add_count || 0) + 1;
  1603. this.initializeElements(node);
  1604. });
  1605. }
  1606. }
  1607. });
  1608. observer.observe(targetNode, observerOptions);
  1609. }
  1610. getRefsProxy() {
  1611. var self = this;
  1612. var refObj = {};
  1613. // One of the goals of this is to not hold elements in memory, but rather re-evaluate
  1614. // the DOM when the system needs something from it. This way, the framework is flexible and
  1615. // friendly to outside DOM changes from libraries like Vue/Livewire.
  1616. // For this reason, I'm using an "on-demand" proxy to fake a "$refs" object.
  1617. return new Proxy(refObj, {
  1618. get(object, property) {
  1619. if (property === '$isAlpineProxy') return true;
  1620. var ref; // We can't just query the DOM because it's hard to filter out refs in
  1621. // nested components.
  1622. self.walkAndSkipNestedComponents(self.$el, el => {
  1623. if (el.hasAttribute('x-ref') && el.getAttribute('x-ref') === property) {
  1624. ref = el;
  1625. }
  1626. });
  1627. return ref;
  1628. }
  1629. });
  1630. }
  1631. }
  1632. const Alpine = {
  1633. version: "2.8.2",
  1634. pauseMutationObserver: false,
  1635. magicProperties: {},
  1636. onComponentInitializeds: [],
  1637. onBeforeComponentInitializeds: [],
  1638. ignoreFocusedForValueBinding: false,
  1639. start: async function start() {
  1640. if (!isTesting()) {
  1641. await domReady();
  1642. }
  1643. this.discoverComponents(el => {
  1644. this.initializeComponent(el);
  1645. }); // It's easier and more performant to just support Turbolinks than listen
  1646. // to MutationObserver mutations at the document level.
  1647. document.addEventListener("turbolinks:load", () => {
  1648. this.discoverUninitializedComponents(el => {
  1649. this.initializeComponent(el);
  1650. });
  1651. });
  1652. this.listenForNewUninitializedComponentsAtRunTime();
  1653. },
  1654. discoverComponents: function discoverComponents(callback) {
  1655. const rootEls = document.querySelectorAll('[x-data]');
  1656. rootEls.forEach(rootEl => {
  1657. callback(rootEl);
  1658. });
  1659. },
  1660. discoverUninitializedComponents: function discoverUninitializedComponents(callback, el = null) {
  1661. const rootEls = (el || document).querySelectorAll('[x-data]');
  1662. Array.from(rootEls).filter(el => el.__x === undefined).forEach(rootEl => {
  1663. callback(rootEl);
  1664. });
  1665. },
  1666. listenForNewUninitializedComponentsAtRunTime: function listenForNewUninitializedComponentsAtRunTime() {
  1667. const targetNode = document.querySelector('body');
  1668. const observerOptions = {
  1669. childList: true,
  1670. attributes: true,
  1671. subtree: true
  1672. };
  1673. const observer = new MutationObserver(mutations => {
  1674. if (this.pauseMutationObserver) return;
  1675. for (let i = 0; i < mutations.length; i++) {
  1676. if (mutations[i].addedNodes.length > 0) {
  1677. mutations[i].addedNodes.forEach(node => {
  1678. // Discard non-element nodes (like line-breaks)
  1679. if (node.nodeType !== 1) return; // Discard any changes happening within an existing component.
  1680. // They will take care of themselves.
  1681. if (node.parentElement && node.parentElement.closest('[x-data]')) return;
  1682. this.discoverUninitializedComponents(el => {
  1683. this.initializeComponent(el);
  1684. }, node.parentElement);
  1685. });
  1686. }
  1687. }
  1688. });
  1689. observer.observe(targetNode, observerOptions);
  1690. },
  1691. initializeComponent: function initializeComponent(el) {
  1692. if (!el.__x) {
  1693. // Wrap in a try/catch so that we don't prevent other components
  1694. // from initializing when one component contains an error.
  1695. try {
  1696. el.__x = new Component(el);
  1697. } catch (error) {
  1698. setTimeout(() => {
  1699. throw error;
  1700. }, 0);
  1701. }
  1702. }
  1703. },
  1704. clone: function clone(component, newEl) {
  1705. if (!newEl.__x) {
  1706. newEl.__x = new Component(newEl, component);
  1707. }
  1708. },
  1709. addMagicProperty: function addMagicProperty(name, callback) {
  1710. this.magicProperties[name] = callback;
  1711. },
  1712. onComponentInitialized: function onComponentInitialized(callback) {
  1713. this.onComponentInitializeds.push(callback);
  1714. },
  1715. onBeforeComponentInitialized: function onBeforeComponentInitialized(callback) {
  1716. this.onBeforeComponentInitializeds.push(callback);
  1717. }
  1718. };
  1719. if (!isTesting()) {
  1720. window.Alpine = Alpine;
  1721. if (window.deferLoadingAlpine) {
  1722. window.deferLoadingAlpine(function () {
  1723. window.Alpine.start();
  1724. });
  1725. } else {
  1726. window.Alpine.start();
  1727. }
  1728. }
  1729. return Alpine;
  1730. })));