visuddhinanda 2 лет назад
Родитель
Сommit
3cd601c1da

+ 80 - 0
app/Http/Controllers/SearchPageNumberController.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Models\PageNumber;
+use App\Models\WbwTemplate;
+use Illuminate\Http\Request;
+
+class SearchPageNumberController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     *
+     * @param  \App\Models\PageNumber  $pageNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function show(string $number)
+    {
+        $pages = PageNumber::where('page',$number)
+                        ->select(['type','volume','page','book','paragraph','pcd_book_id'])
+                        ->get();
+        $para = WbwTemplate::where('real','para'.$number)->select(['book','paragraph','pcd_book_id'])->get();
+        foreach ($para as $key => $value) {
+            # code...
+            $pages[] = [
+                'type'=>'para',
+                'volume'=>0,
+                'page'=>$number,
+                'book'=>$value->book,
+                'paragraph'=>$value->paragraph,
+                'pcd_book_id'=>$value->pcd_book_id,
+            ];
+        }
+        return $this->ok($pages);
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \App\Models\PageNumber  $pageNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function update(Request $request, PageNumber $pageNumber)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\Models\PageNumber  $pageNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function destroy(PageNumber $pageNumber)
+    {
+        //
+    }
+}

BIN
public/assets/gallery/01.jpg


BIN
public/assets/gallery/02.jpg


BIN
public/assets/gallery/03.jpg


BIN
public/assets/gallery/04.jpg


BIN
public/assets/gallery/05.jpg


BIN
public/assets/images/hero-1.jpg


BIN
public/assets/images/hero.jpg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/assets/typhoon/css/form-styles.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/assets/typhoon/css/glightbox.min.css


+ 1 - 0
public/assets/typhoon/css/notices.css

@@ -0,0 +1 @@
+.notices{padding:1px 1px 1px 30px;margin:15px 0}.notices p{}.notices.yellow{border-left:10px solid #f0ad4e;background:#fcf8f2;color:#df8a13}.notices.red{border-left:10px solid #d9534f;background:#fdf7f7;color:#b52b27}.notices.blue{border-left:10px solid #5bc0de;background:#f4f8fa;color:#28a1c5}.notices.green{border-left:10px solid #5cb85c;background:#f1f9f1;color:#3d8b3d}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/assets/typhoon/css/site.css


+ 1956 - 0
public/assets/typhoon/js/alpine.js

@@ -0,0 +1,1956 @@
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+  typeof define === 'function' && define.amd ? define(factory) :
+  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alpine = factory());
+}(this, (function () { 'use strict';
+
+  function _defineProperty(obj, key, value) {
+    if (key in obj) {
+      Object.defineProperty(obj, key, {
+        value: value,
+        enumerable: true,
+        configurable: true,
+        writable: true
+      });
+    } else {
+      obj[key] = value;
+    }
+
+    return obj;
+  }
+
+  function ownKeys(object, enumerableOnly) {
+    var keys = Object.keys(object);
+
+    if (Object.getOwnPropertySymbols) {
+      var symbols = Object.getOwnPropertySymbols(object);
+      if (enumerableOnly) symbols = symbols.filter(function (sym) {
+        return Object.getOwnPropertyDescriptor(object, sym).enumerable;
+      });
+      keys.push.apply(keys, symbols);
+    }
+
+    return keys;
+  }
+
+  function _objectSpread2(target) {
+    for (var i = 1; i < arguments.length; i++) {
+      var source = arguments[i] != null ? arguments[i] : {};
+
+      if (i % 2) {
+        ownKeys(Object(source), true).forEach(function (key) {
+          _defineProperty(target, key, source[key]);
+        });
+      } else if (Object.getOwnPropertyDescriptors) {
+        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
+      } else {
+        ownKeys(Object(source)).forEach(function (key) {
+          Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
+        });
+      }
+    }
+
+    return target;
+  }
+
+  // Thanks @stimulus:
+  // https://github.com/stimulusjs/stimulus/blob/master/packages/%40stimulus/core/src/application.ts
+  function domReady() {
+    return new Promise(resolve => {
+      if (document.readyState == "loading") {
+        document.addEventListener("DOMContentLoaded", resolve);
+      } else {
+        resolve();
+      }
+    });
+  }
+  function arrayUnique(array) {
+    return Array.from(new Set(array));
+  }
+  function isTesting() {
+    return navigator.userAgent.includes("Node.js") || navigator.userAgent.includes("jsdom");
+  }
+  function checkedAttrLooseCompare(valueA, valueB) {
+    return valueA == valueB;
+  }
+  function warnIfMalformedTemplate(el, directive) {
+    if (el.tagName.toLowerCase() !== 'template') {
+      console.warn(`Alpine: [${directive}] directive should only be added to <template> tags. See https://github.com/alpinejs/alpine#${directive}`);
+    } else if (el.content.childElementCount !== 1) {
+      console.warn(`Alpine: <template> tag with [${directive}] encountered with an unexpected number of root elements. Make sure <template> has a single root element. `);
+    }
+  }
+  function kebabCase(subject) {
+    return subject.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[_\s]/, '-').toLowerCase();
+  }
+  function camelCase(subject) {
+    return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase());
+  }
+  function walk(el, callback) {
+    if (callback(el) === false) return;
+    let node = el.firstElementChild;
+
+    while (node) {
+      walk(node, callback);
+      node = node.nextElementSibling;
+    }
+  }
+  function debounce(func, wait) {
+    var timeout;
+    return function () {
+      var context = this,
+          args = arguments;
+
+      var later = function later() {
+        timeout = null;
+        func.apply(context, args);
+      };
+
+      clearTimeout(timeout);
+      timeout = setTimeout(later, wait);
+    };
+  }
+
+  const handleError = (el, expression, error) => {
+    console.warn(`Alpine Error: "${error}"\n\nExpression: "${expression}"\nElement:`, el);
+
+    if (!isTesting()) {
+      Object.assign(error, {
+        el,
+        expression
+      });
+      throw error;
+    }
+  };
+
+  function tryCatch(cb, {
+    el,
+    expression
+  }) {
+    try {
+      const value = cb();
+      return value instanceof Promise ? value.catch(e => handleError(el, expression, e)) : value;
+    } catch (e) {
+      handleError(el, expression, e);
+    }
+  }
+
+  function saferEval(el, expression, dataContext, additionalHelperVariables = {}) {
+    return tryCatch(() => {
+      if (typeof expression === 'function') {
+        return expression.call(dataContext);
+      }
+
+      return new Function(['$data', ...Object.keys(additionalHelperVariables)], `var __alpine_result; with($data) { __alpine_result = ${expression} }; return __alpine_result`)(dataContext, ...Object.values(additionalHelperVariables));
+    }, {
+      el,
+      expression
+    });
+  }
+  function saferEvalNoReturn(el, expression, dataContext, additionalHelperVariables = {}) {
+    return tryCatch(() => {
+      if (typeof expression === 'function') {
+        return Promise.resolve(expression.call(dataContext, additionalHelperVariables['$event']));
+      }
+
+      let AsyncFunction = Function;
+      /* MODERN-ONLY:START */
+
+      AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
+      /* MODERN-ONLY:END */
+      // For the cases when users pass only a function reference to the caller: `x-on:click="foo"`
+      // Where "foo" is a function. Also, we'll pass the function the event instance when we call it.
+
+      if (Object.keys(dataContext).includes(expression)) {
+        let methodReference = new Function(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { return ${expression} }`)(dataContext, ...Object.values(additionalHelperVariables));
+
+        if (typeof methodReference === 'function') {
+          return Promise.resolve(methodReference.call(dataContext, additionalHelperVariables['$event']));
+        } else {
+          return Promise.resolve();
+        }
+      }
+
+      return Promise.resolve(new AsyncFunction(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { ${expression} }`)(dataContext, ...Object.values(additionalHelperVariables)));
+    }, {
+      el,
+      expression
+    });
+  }
+  const xAttrRE = /^x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref|spread)\b/;
+  function isXAttr(attr) {
+    const name = replaceAtAndColonWithStandardSyntax(attr.name);
+    return xAttrRE.test(name);
+  }
+  function getXAttrs(el, component, type) {
+    let directives = Array.from(el.attributes).filter(isXAttr).map(parseHtmlAttribute); // Get an object of directives from x-spread.
+
+    let spreadDirective = directives.filter(directive => directive.type === 'spread')[0];
+
+    if (spreadDirective) {
+      let spreadObject = saferEval(el, spreadDirective.expression, component.$data); // Add x-spread directives to the pile of existing directives.
+
+      directives = directives.concat(Object.entries(spreadObject).map(([name, value]) => parseHtmlAttribute({
+        name,
+        value
+      })));
+    }
+
+    if (type) return directives.filter(i => i.type === type);
+    return sortDirectives(directives);
+  }
+
+  function sortDirectives(directives) {
+    let directiveOrder = ['bind', 'model', 'show', 'catch-all'];
+    return directives.sort((a, b) => {
+      let typeA = directiveOrder.indexOf(a.type) === -1 ? 'catch-all' : a.type;
+      let typeB = directiveOrder.indexOf(b.type) === -1 ? 'catch-all' : b.type;
+      return directiveOrder.indexOf(typeA) - directiveOrder.indexOf(typeB);
+    });
+  }
+
+  function parseHtmlAttribute({
+    name,
+    value
+  }) {
+    const normalizedName = replaceAtAndColonWithStandardSyntax(name);
+    const typeMatch = normalizedName.match(xAttrRE);
+    const valueMatch = normalizedName.match(/:([a-zA-Z0-9\-:]+)/);
+    const modifiers = normalizedName.match(/\.[^.\]]+(?=[^\]]*$)/g) || [];
+    return {
+      type: typeMatch ? typeMatch[1] : null,
+      value: valueMatch ? valueMatch[1] : null,
+      modifiers: modifiers.map(i => i.replace('.', '')),
+      expression: value
+    };
+  }
+  function isBooleanAttr(attrName) {
+    // As per HTML spec table https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute
+    // Array roughly ordered by estimated usage
+    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'];
+    return booleanAttributes.includes(attrName);
+  }
+  function replaceAtAndColonWithStandardSyntax(name) {
+    if (name.startsWith('@')) {
+      return name.replace('@', 'x-on:');
+    } else if (name.startsWith(':')) {
+      return name.replace(':', 'x-bind:');
+    }
+
+    return name;
+  }
+  function convertClassStringToArray(classList, filterFn = Boolean) {
+    return classList.split(' ').filter(filterFn);
+  }
+  const TRANSITION_TYPE_IN = 'in';
+  const TRANSITION_TYPE_OUT = 'out';
+  const TRANSITION_CANCELLED = 'cancelled';
+  function transitionIn(el, show, reject, component, forceSkip = false) {
+    // We don't want to transition on the initial page load.
+    if (forceSkip) return show();
+
+    if (el.__x_transition && el.__x_transition.type === TRANSITION_TYPE_IN) {
+      // there is already a similar transition going on, this was probably triggered by
+      // a change in a different property, let's just leave the previous one doing its job
+      return;
+    }
+
+    const attrs = getXAttrs(el, component, 'transition');
+    const showAttr = getXAttrs(el, component, 'show')[0]; // If this is triggered by a x-show.transition.
+
+    if (showAttr && showAttr.modifiers.includes('transition')) {
+      let modifiers = showAttr.modifiers; // If x-show.transition.out, we'll skip the "in" transition.
+
+      if (modifiers.includes('out') && !modifiers.includes('in')) return show();
+      const settingBothSidesOfTransition = modifiers.includes('in') && modifiers.includes('out'); // If x-show.transition.in...out... only use "in" related modifiers for this transition.
+
+      modifiers = settingBothSidesOfTransition ? modifiers.filter((i, index) => index < modifiers.indexOf('out')) : modifiers;
+      transitionHelperIn(el, modifiers, show, reject); // Otherwise, we can assume x-transition:enter.
+    } else if (attrs.some(attr => ['enter', 'enter-start', 'enter-end'].includes(attr.value))) {
+      transitionClassesIn(el, component, attrs, show, reject);
+    } else {
+      // If neither, just show that damn thing.
+      show();
+    }
+  }
+  function transitionOut(el, hide, reject, component, forceSkip = false) {
+    // We don't want to transition on the initial page load.
+    if (forceSkip) return hide();
+
+    if (el.__x_transition && el.__x_transition.type === TRANSITION_TYPE_OUT) {
+      // there is already a similar transition going on, this was probably triggered by
+      // a change in a different property, let's just leave the previous one doing its job
+      return;
+    }
+
+    const attrs = getXAttrs(el, component, 'transition');
+    const showAttr = getXAttrs(el, component, 'show')[0];
+
+    if (showAttr && showAttr.modifiers.includes('transition')) {
+      let modifiers = showAttr.modifiers;
+      if (modifiers.includes('in') && !modifiers.includes('out')) return hide();
+      const settingBothSidesOfTransition = modifiers.includes('in') && modifiers.includes('out');
+      modifiers = settingBothSidesOfTransition ? modifiers.filter((i, index) => index > modifiers.indexOf('out')) : modifiers;
+      transitionHelperOut(el, modifiers, settingBothSidesOfTransition, hide, reject);
+    } else if (attrs.some(attr => ['leave', 'leave-start', 'leave-end'].includes(attr.value))) {
+      transitionClassesOut(el, component, attrs, hide, reject);
+    } else {
+      hide();
+    }
+  }
+  function transitionHelperIn(el, modifiers, showCallback, reject) {
+    // Default values inspired by: https://material.io/design/motion/speed.html#duration
+    const styleValues = {
+      duration: modifierValue(modifiers, 'duration', 150),
+      origin: modifierValue(modifiers, 'origin', 'center'),
+      first: {
+        opacity: 0,
+        scale: modifierValue(modifiers, 'scale', 95)
+      },
+      second: {
+        opacity: 1,
+        scale: 100
+      }
+    };
+    transitionHelper(el, modifiers, showCallback, () => {}, reject, styleValues, TRANSITION_TYPE_IN);
+  }
+  function transitionHelperOut(el, modifiers, settingBothSidesOfTransition, hideCallback, reject) {
+    // Make the "out" transition .5x slower than the "in". (Visually better)
+    // HOWEVER, if they explicitly set a duration for the "out" transition,
+    // use that.
+    const duration = settingBothSidesOfTransition ? modifierValue(modifiers, 'duration', 150) : modifierValue(modifiers, 'duration', 150) / 2;
+    const styleValues = {
+      duration: duration,
+      origin: modifierValue(modifiers, 'origin', 'center'),
+      first: {
+        opacity: 1,
+        scale: 100
+      },
+      second: {
+        opacity: 0,
+        scale: modifierValue(modifiers, 'scale', 95)
+      }
+    };
+    transitionHelper(el, modifiers, () => {}, hideCallback, reject, styleValues, TRANSITION_TYPE_OUT);
+  }
+
+  function modifierValue(modifiers, key, fallback) {
+    // If the modifier isn't present, use the default.
+    if (modifiers.indexOf(key) === -1) return fallback; // If it IS present, grab the value after it: x-show.transition.duration.500ms
+
+    const rawValue = modifiers[modifiers.indexOf(key) + 1];
+    if (!rawValue) return fallback;
+
+    if (key === 'scale') {
+      // Check if the very next value is NOT a number and return the fallback.
+      // If x-show.transition.scale, we'll use the default scale value.
+      // That is how a user opts out of the opacity transition.
+      if (!isNumeric(rawValue)) return fallback;
+    }
+
+    if (key === 'duration') {
+      // Support x-show.transition.duration.500ms && duration.500
+      let match = rawValue.match(/([0-9]+)ms/);
+      if (match) return match[1];
+    }
+
+    if (key === 'origin') {
+      // Support chaining origin directions: x-show.transition.top.right
+      if (['top', 'right', 'left', 'center', 'bottom'].includes(modifiers[modifiers.indexOf(key) + 2])) {
+        return [rawValue, modifiers[modifiers.indexOf(key) + 2]].join(' ');
+      }
+    }
+
+    return rawValue;
+  }
+
+  function transitionHelper(el, modifiers, hook1, hook2, reject, styleValues, type) {
+    // clear the previous transition if exists to avoid caching the wrong styles
+    if (el.__x_transition) {
+      el.__x_transition.cancel && el.__x_transition.cancel();
+    } // If the user set these style values, we'll put them back when we're done with them.
+
+
+    const opacityCache = el.style.opacity;
+    const transformCache = el.style.transform;
+    const transformOriginCache = el.style.transformOrigin; // If no modifiers are present: x-show.transition, we'll default to both opacity and scale.
+
+    const noModifiers = !modifiers.includes('opacity') && !modifiers.includes('scale');
+    const transitionOpacity = noModifiers || modifiers.includes('opacity');
+    const transitionScale = noModifiers || modifiers.includes('scale'); // These are the explicit stages of a transition (same stages for in and for out).
+    // This way you can get a birds eye view of the hooks, and the differences
+    // between them.
+
+    const stages = {
+      start() {
+        if (transitionOpacity) el.style.opacity = styleValues.first.opacity;
+        if (transitionScale) el.style.transform = `scale(${styleValues.first.scale / 100})`;
+      },
+
+      during() {
+        if (transitionScale) el.style.transformOrigin = styleValues.origin;
+        el.style.transitionProperty = [transitionOpacity ? `opacity` : ``, transitionScale ? `transform` : ``].join(' ').trim();
+        el.style.transitionDuration = `${styleValues.duration / 1000}s`;
+        el.style.transitionTimingFunction = `cubic-bezier(0.4, 0.0, 0.2, 1)`;
+      },
+
+      show() {
+        hook1();
+      },
+
+      end() {
+        if (transitionOpacity) el.style.opacity = styleValues.second.opacity;
+        if (transitionScale) el.style.transform = `scale(${styleValues.second.scale / 100})`;
+      },
+
+      hide() {
+        hook2();
+      },
+
+      cleanup() {
+        if (transitionOpacity) el.style.opacity = opacityCache;
+        if (transitionScale) el.style.transform = transformCache;
+        if (transitionScale) el.style.transformOrigin = transformOriginCache;
+        el.style.transitionProperty = null;
+        el.style.transitionDuration = null;
+        el.style.transitionTimingFunction = null;
+      }
+
+    };
+    transition(el, stages, type, reject);
+  }
+
+  const ensureStringExpression = (expression, el, component) => {
+    return typeof expression === 'function' ? component.evaluateReturnExpression(el, expression) : expression;
+  };
+
+  function transitionClassesIn(el, component, directives, showCallback, reject) {
+    const enter = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'enter') || {
+      expression: ''
+    }).expression, el, component));
+    const enterStart = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'enter-start') || {
+      expression: ''
+    }).expression, el, component));
+    const enterEnd = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'enter-end') || {
+      expression: ''
+    }).expression, el, component));
+    transitionClasses(el, enter, enterStart, enterEnd, showCallback, () => {}, TRANSITION_TYPE_IN, reject);
+  }
+  function transitionClassesOut(el, component, directives, hideCallback, reject) {
+    const leave = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'leave') || {
+      expression: ''
+    }).expression, el, component));
+    const leaveStart = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'leave-start') || {
+      expression: ''
+    }).expression, el, component));
+    const leaveEnd = convertClassStringToArray(ensureStringExpression((directives.find(i => i.value === 'leave-end') || {
+      expression: ''
+    }).expression, el, component));
+    transitionClasses(el, leave, leaveStart, leaveEnd, () => {}, hideCallback, TRANSITION_TYPE_OUT, reject);
+  }
+  function transitionClasses(el, classesDuring, classesStart, classesEnd, hook1, hook2, type, reject) {
+    // clear the previous transition if exists to avoid caching the wrong classes
+    if (el.__x_transition) {
+      el.__x_transition.cancel && el.__x_transition.cancel();
+    }
+
+    const originalClasses = el.__x_original_classes || [];
+    const stages = {
+      start() {
+        el.classList.add(...classesStart);
+      },
+
+      during() {
+        el.classList.add(...classesDuring);
+      },
+
+      show() {
+        hook1();
+      },
+
+      end() {
+        // Don't remove classes that were in the original class attribute.
+        el.classList.remove(...classesStart.filter(i => !originalClasses.includes(i)));
+        el.classList.add(...classesEnd);
+      },
+
+      hide() {
+        hook2();
+      },
+
+      cleanup() {
+        el.classList.remove(...classesDuring.filter(i => !originalClasses.includes(i)));
+        el.classList.remove(...classesEnd.filter(i => !originalClasses.includes(i)));
+      }
+
+    };
+    transition(el, stages, type, reject);
+  }
+  function transition(el, stages, type, reject) {
+    const finish = once(() => {
+      stages.hide(); // Adding an "isConnected" check, in case the callback
+      // removed the element from the DOM.
+
+      if (el.isConnected) {
+        stages.cleanup();
+      }
+
+      delete el.__x_transition;
+    });
+    el.__x_transition = {
+      // Set transition type so we can avoid clearing transition if the direction is the same
+      type: type,
+      // create a callback for the last stages of the transition so we can call it
+      // from different point and early terminate it. Once will ensure that function
+      // is only called one time.
+      cancel: once(() => {
+        reject(TRANSITION_CANCELLED);
+        finish();
+      }),
+      finish,
+      // This store the next animation frame so we can cancel it
+      nextFrame: null
+    };
+    stages.start();
+    stages.during();
+    el.__x_transition.nextFrame = requestAnimationFrame(() => {
+      // Note: Safari's transitionDuration property will list out comma separated transition durations
+      // for every single transition property. Let's grab the first one and call it a day.
+      let duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, '').replace('s', '')) * 1000;
+
+      if (duration === 0) {
+        duration = Number(getComputedStyle(el).animationDuration.replace('s', '')) * 1000;
+      }
+
+      stages.show();
+      el.__x_transition.nextFrame = requestAnimationFrame(() => {
+        stages.end();
+        setTimeout(el.__x_transition.finish, duration);
+      });
+    });
+  }
+  function isNumeric(subject) {
+    return !Array.isArray(subject) && !isNaN(subject);
+  } // Thanks @vuejs
+  // https://github.com/vuejs/vue/blob/4de4649d9637262a9b007720b59f80ac72a5620c/src/shared/util.js
+
+  function once(callback) {
+    let called = false;
+    return function () {
+      if (!called) {
+        called = true;
+        callback.apply(this, arguments);
+      }
+    };
+  }
+
+  function handleForDirective(component, templateEl, expression, initialUpdate, extraVars) {
+    warnIfMalformedTemplate(templateEl, 'x-for');
+    let iteratorNames = typeof expression === 'function' ? parseForExpression(component.evaluateReturnExpression(templateEl, expression)) : parseForExpression(expression);
+    let items = evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, templateEl, iteratorNames, extraVars); // As we walk the array, we'll also walk the DOM (updating/creating as we go).
+
+    let currentEl = templateEl;
+    items.forEach((item, index) => {
+      let iterationScopeVariables = getIterationScopeVariables(iteratorNames, item, index, items, extraVars());
+      let currentKey = generateKeyForIteration(component, templateEl, index, iterationScopeVariables);
+      let nextEl = lookAheadForMatchingKeyedElementAndMoveItIfFound(currentEl.nextElementSibling, currentKey); // If we haven't found a matching key, insert the element at the current position.
+
+      if (!nextEl) {
+        nextEl = addElementInLoopAfterCurrentEl(templateEl, currentEl); // And transition it in if it's not the first page load.
+
+        transitionIn(nextEl, () => {}, () => {}, component, initialUpdate);
+        nextEl.__x_for = iterationScopeVariables;
+        component.initializeElements(nextEl, () => nextEl.__x_for); // Otherwise update the element we found.
+      } else {
+        // Temporarily remove the key indicator to allow the normal "updateElements" to work.
+        delete nextEl.__x_for_key;
+        nextEl.__x_for = iterationScopeVariables;
+        component.updateElements(nextEl, () => nextEl.__x_for);
+      }
+
+      currentEl = nextEl;
+      currentEl.__x_for_key = currentKey;
+    });
+    removeAnyLeftOverElementsFromPreviousUpdate(currentEl, component);
+  } // This was taken from VueJS 2.* core. Thanks Vue!
+
+  function parseForExpression(expression) {
+    let forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
+    let stripParensRE = /^\(|\)$/g;
+    let forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
+    let inMatch = String(expression).match(forAliasRE);
+    if (!inMatch) return;
+    let res = {};
+    res.items = inMatch[2].trim();
+    let item = inMatch[1].trim().replace(stripParensRE, '');
+    let iteratorMatch = item.match(forIteratorRE);
+
+    if (iteratorMatch) {
+      res.item = item.replace(forIteratorRE, '').trim();
+      res.index = iteratorMatch[1].trim();
+
+      if (iteratorMatch[2]) {
+        res.collection = iteratorMatch[2].trim();
+      }
+    } else {
+      res.item = item;
+    }
+
+    return res;
+  }
+
+  function getIterationScopeVariables(iteratorNames, item, index, items, extraVars) {
+    // We must create a new object, so each iteration has a new scope
+    let scopeVariables = extraVars ? _objectSpread2({}, extraVars) : {};
+    scopeVariables[iteratorNames.item] = item;
+    if (iteratorNames.index) scopeVariables[iteratorNames.index] = index;
+    if (iteratorNames.collection) scopeVariables[iteratorNames.collection] = items;
+    return scopeVariables;
+  }
+
+  function generateKeyForIteration(component, el, index, iterationScopeVariables) {
+    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.
+
+    if (!bindKeyAttribute) return index;
+    return component.evaluateReturnExpression(el, bindKeyAttribute.expression, () => iterationScopeVariables);
+  }
+
+  function evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, el, iteratorNames, extraVars) {
+    let ifAttribute = getXAttrs(el, component, 'if')[0];
+
+    if (ifAttribute && !component.evaluateReturnExpression(el, ifAttribute.expression)) {
+      return [];
+    }
+
+    let items = component.evaluateReturnExpression(el, iteratorNames.items, extraVars); // This adds support for the `i in n` syntax.
+
+    if (isNumeric(items) && items >= 0) {
+      items = Array.from(Array(items).keys(), i => i + 1);
+    }
+
+    return items;
+  }
+
+  function addElementInLoopAfterCurrentEl(templateEl, currentEl) {
+    let clone = document.importNode(templateEl.content, true);
+    currentEl.parentElement.insertBefore(clone, currentEl.nextElementSibling);
+    return currentEl.nextElementSibling;
+  }
+
+  function lookAheadForMatchingKeyedElementAndMoveItIfFound(nextEl, currentKey) {
+    if (!nextEl) return; // If we are already past the x-for generated elements, we don't need to look ahead.
+
+    if (nextEl.__x_for_key === undefined) return; // If the the key's DO match, no need to look ahead.
+
+    if (nextEl.__x_for_key === currentKey) return nextEl; // If they don't, we'll look ahead for a match.
+    // If we find it, we'll move it to the current position in the loop.
+
+    let tmpNextEl = nextEl;
+
+    while (tmpNextEl) {
+      if (tmpNextEl.__x_for_key === currentKey) {
+        return tmpNextEl.parentElement.insertBefore(tmpNextEl, nextEl);
+      }
+
+      tmpNextEl = tmpNextEl.nextElementSibling && tmpNextEl.nextElementSibling.__x_for_key !== undefined ? tmpNextEl.nextElementSibling : false;
+    }
+  }
+
+  function removeAnyLeftOverElementsFromPreviousUpdate(currentEl, component) {
+    var nextElementFromOldLoop = currentEl.nextElementSibling && currentEl.nextElementSibling.__x_for_key !== undefined ? currentEl.nextElementSibling : false;
+
+    while (nextElementFromOldLoop) {
+      let nextElementFromOldLoopImmutable = nextElementFromOldLoop;
+      let nextSibling = nextElementFromOldLoop.nextElementSibling;
+      transitionOut(nextElementFromOldLoop, () => {
+        nextElementFromOldLoopImmutable.remove();
+      }, () => {}, component);
+      nextElementFromOldLoop = nextSibling && nextSibling.__x_for_key !== undefined ? nextSibling : false;
+    }
+  }
+
+  function handleAttributeBindingDirective(component, el, attrName, expression, extraVars, attrType, modifiers) {
+    var value = component.evaluateReturnExpression(el, expression, extraVars);
+
+    if (attrName === 'value') {
+      if (Alpine.ignoreFocusedForValueBinding && document.activeElement.isSameNode(el)) return; // If nested model key is undefined, set the default value to empty string.
+
+      if (value === undefined && String(expression).match(/\./)) {
+        value = '';
+      }
+
+      if (el.type === 'radio') {
+        // Set radio value from x-bind:value, if no "value" attribute exists.
+        // If there are any initial state values, radio will have a correct
+        // "checked" value since x-bind:value is processed before x-model.
+        if (el.attributes.value === undefined && attrType === 'bind') {
+          el.value = value;
+        } else if (attrType !== 'bind') {
+          el.checked = checkedAttrLooseCompare(el.value, value);
+        }
+      } else if (el.type === 'checkbox') {
+        // If we are explicitly binding a string to the :value, set the string,
+        // If the value is a boolean, leave it alone, it will be set to "on"
+        // automatically.
+        if (typeof value !== 'boolean' && ![null, undefined].includes(value) && attrType === 'bind') {
+          el.value = String(value);
+        } else if (attrType !== 'bind') {
+          if (Array.isArray(value)) {
+            // I'm purposely not using Array.includes here because it's
+            // strict, and because of Numeric/String mis-casting, I
+            // want the "includes" to be "fuzzy".
+            el.checked = value.some(val => checkedAttrLooseCompare(val, el.value));
+          } else {
+            el.checked = !!value;
+          }
+        }
+      } else if (el.tagName === 'SELECT') {
+        updateSelect(el, value);
+      } else {
+        if (el.value === value) return;
+        el.value = value;
+      }
+    } else if (attrName === 'class') {
+      if (Array.isArray(value)) {
+        const originalClasses = el.__x_original_classes || [];
+        el.setAttribute('class', arrayUnique(originalClasses.concat(value)).join(' '));
+      } else if (typeof value === 'object') {
+        // Sorting the keys / class names by their boolean value will ensure that
+        // anything that evaluates to `false` and needs to remove classes is run first.
+        const keysSortedByBooleanValue = Object.keys(value).sort((a, b) => value[a] - value[b]);
+        keysSortedByBooleanValue.forEach(classNames => {
+          if (value[classNames]) {
+            convertClassStringToArray(classNames).forEach(className => el.classList.add(className));
+          } else {
+            convertClassStringToArray(classNames).forEach(className => el.classList.remove(className));
+          }
+        });
+      } else {
+        const originalClasses = el.__x_original_classes || [];
+        const newClasses = value ? convertClassStringToArray(value) : [];
+        el.setAttribute('class', arrayUnique(originalClasses.concat(newClasses)).join(' '));
+      }
+    } else {
+      attrName = modifiers.includes('camel') ? camelCase(attrName) : attrName; // If an attribute's bound value is null, undefined or false, remove the attribute
+
+      if ([null, undefined, false].includes(value)) {
+        el.removeAttribute(attrName);
+      } else {
+        isBooleanAttr(attrName) ? setIfChanged(el, attrName, attrName) : setIfChanged(el, attrName, value);
+      }
+    }
+  }
+
+  function setIfChanged(el, attrName, value) {
+    if (el.getAttribute(attrName) != value) {
+      el.setAttribute(attrName, value);
+    }
+  }
+
+  function updateSelect(el, value) {
+    const arrayWrappedValue = [].concat(value).map(value => {
+      return value + '';
+    });
+    Array.from(el.options).forEach(option => {
+      option.selected = arrayWrappedValue.includes(option.value);
+    });
+  }
+
+  function handleTextDirective(el, output, expression) {
+    // If nested model key is undefined, set the default value to empty string.
+    if (output === undefined && String(expression).match(/\./)) {
+      output = '';
+    }
+
+    el.textContent = output;
+  }
+
+  function handleHtmlDirective(component, el, expression, extraVars) {
+    el.innerHTML = component.evaluateReturnExpression(el, expression, extraVars);
+  }
+
+  function handleShowDirective(component, el, value, modifiers, initialUpdate = false) {
+    const hide = () => {
+      el.style.display = 'none';
+      el.__x_is_shown = false;
+    };
+
+    const show = () => {
+      if (el.style.length === 1 && el.style.display === 'none') {
+        el.removeAttribute('style');
+      } else {
+        el.style.removeProperty('display');
+      }
+
+      el.__x_is_shown = true;
+    };
+
+    if (initialUpdate === true) {
+      if (value) {
+        show();
+      } else {
+        hide();
+      }
+
+      return;
+    }
+
+    const handle = (resolve, reject) => {
+      if (value) {
+        if (el.style.display === 'none' || el.__x_transition) {
+          transitionIn(el, () => {
+            show();
+          }, reject, component);
+        }
+
+        resolve(() => {});
+      } else {
+        if (el.style.display !== 'none') {
+          transitionOut(el, () => {
+            resolve(() => {
+              hide();
+            });
+          }, reject, component);
+        } else {
+          resolve(() => {});
+        }
+      }
+    }; // The working of x-show is a bit complex because we need to
+    // wait for any child transitions to finish before hiding
+    // some element. Also, this has to be done recursively.
+    // If x-show.immediate, foregoe the waiting.
+
+
+    if (modifiers.includes('immediate')) {
+      handle(finish => finish(), () => {});
+      return;
+    } // x-show is encountered during a DOM tree walk. If an element
+    // we encounter is NOT a child of another x-show element we
+    // can execute the previous x-show stack (if one exists).
+
+
+    if (component.showDirectiveLastElement && !component.showDirectiveLastElement.contains(el)) {
+      component.executeAndClearRemainingShowDirectiveStack();
+    }
+
+    component.showDirectiveStack.push(handle);
+    component.showDirectiveLastElement = el;
+  }
+
+  function handleIfDirective(component, el, expressionResult, initialUpdate, extraVars) {
+    warnIfMalformedTemplate(el, 'x-if');
+    const elementHasAlreadyBeenAdded = el.nextElementSibling && el.nextElementSibling.__x_inserted_me === true;
+
+    if (expressionResult && (!elementHasAlreadyBeenAdded || el.__x_transition)) {
+      const clone = document.importNode(el.content, true);
+      el.parentElement.insertBefore(clone, el.nextElementSibling);
+      transitionIn(el.nextElementSibling, () => {}, () => {}, component, initialUpdate);
+      component.initializeElements(el.nextElementSibling, extraVars);
+      el.nextElementSibling.__x_inserted_me = true;
+    } else if (!expressionResult && elementHasAlreadyBeenAdded) {
+      transitionOut(el.nextElementSibling, () => {
+        el.nextElementSibling.remove();
+      }, () => {}, component, initialUpdate);
+    }
+  }
+
+  function registerListener(component, el, event, modifiers, expression, extraVars = {}) {
+    const options = {
+      passive: modifiers.includes('passive')
+    };
+
+    if (modifiers.includes('camel')) {
+      event = camelCase(event);
+    }
+
+    const node_add_count = el.__x_node_add_count;
+    let handler, listenerTarget;
+
+    if (modifiers.includes('away')) {
+      listenerTarget = document;
+
+      handler = e => {
+        // Don't do anything if the click came from the element or within it.
+        if (el.contains(e.target)) return; // Don't do anything if this element isn't currently visible.
+
+        if (el.offsetWidth < 1 && el.offsetHeight < 1) return; // Now that we are sure the element is visible, AND the click
+        // is from outside it, let's run the expression.
+
+        runListenerHandler(component, expression, e, extraVars);
+
+        if (modifiers.includes('once')) {
+          document.removeEventListener(event, handler, options);
+        }
+      };
+    } else {
+      listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
+
+      handler = e => {
+        // Remove this global event handler if the element that declared it
+        // has been removed. It's now stale.
+        if (listenerTarget === window || listenerTarget === document) {
+          if (!document.body.contains(el)) {
+            listenerTarget.removeEventListener(event, handler, options);
+            return;
+          }
+        }
+
+        if (el.__x_node_add_count !== node_add_count) {
+          listenerTarget.removeEventListener(event, handler, options);
+          return;
+        }
+
+        if (isKeyEvent(event)) {
+          if (isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers)) {
+            return;
+          }
+        }
+
+        if (modifiers.includes('prevent')) e.preventDefault();
+        if (modifiers.includes('stop')) e.stopPropagation(); // If the .self modifier isn't present, or if it is present and
+        // the target element matches the element we are registering the
+        // event on, run the handler
+
+        if (!modifiers.includes('self') || e.target === el) {
+          const returnValue = runListenerHandler(component, expression, e, extraVars);
+          returnValue.then(value => {
+            if (value === false) {
+              e.preventDefault();
+            } else {
+              if (modifiers.includes('once')) {
+                listenerTarget.removeEventListener(event, handler, options);
+              }
+            }
+          });
+        }
+      };
+    }
+
+    if (modifiers.includes('debounce')) {
+      let nextModifier = modifiers[modifiers.indexOf('debounce') + 1] || 'invalid-wait';
+      let wait = isNumeric(nextModifier.split('ms')[0]) ? Number(nextModifier.split('ms')[0]) : 250;
+      handler = debounce(handler, wait);
+    }
+
+    listenerTarget.addEventListener(event, handler, options);
+  }
+
+  function runListenerHandler(component, expression, e, extraVars) {
+    return component.evaluateCommandExpression(e.target, expression, () => {
+      return _objectSpread2(_objectSpread2({}, extraVars()), {}, {
+        '$event': e
+      });
+    });
+  }
+
+  function isKeyEvent(event) {
+    return ['keydown', 'keyup'].includes(event);
+  }
+
+  function isListeningForASpecificKeyThatHasntBeenPressed(e, modifiers) {
+    let keyModifiers = modifiers.filter(i => {
+      return !['window', 'document', 'prevent', 'stop'].includes(i);
+    });
+
+    if (keyModifiers.includes('debounce')) {
+      let debounceIndex = keyModifiers.indexOf('debounce');
+      keyModifiers.splice(debounceIndex, isNumeric((keyModifiers[debounceIndex + 1] || 'invalid-wait').split('ms')[0]) ? 2 : 1);
+    } // If no modifier is specified, we'll call it a press.
+
+
+    if (keyModifiers.length === 0) return false; // If one is passed, AND it matches the key pressed, we'll call it a press.
+
+    if (keyModifiers.length === 1 && keyModifiers[0] === keyToModifier(e.key)) return false; // The user is listening for key combinations.
+
+    const systemKeyModifiers = ['ctrl', 'shift', 'alt', 'meta', 'cmd', 'super'];
+    const selectedSystemKeyModifiers = systemKeyModifiers.filter(modifier => keyModifiers.includes(modifier));
+    keyModifiers = keyModifiers.filter(i => !selectedSystemKeyModifiers.includes(i));
+
+    if (selectedSystemKeyModifiers.length > 0) {
+      const activelyPressedKeyModifiers = selectedSystemKeyModifiers.filter(modifier => {
+        // Alias "cmd" and "super" to "meta"
+        if (modifier === 'cmd' || modifier === 'super') modifier = 'meta';
+        return e[`${modifier}Key`];
+      }); // If all the modifiers selected are pressed, ...
+
+      if (activelyPressedKeyModifiers.length === selectedSystemKeyModifiers.length) {
+        // AND the remaining key is pressed as well. It's a press.
+        if (keyModifiers[0] === keyToModifier(e.key)) return false;
+      }
+    } // We'll call it NOT a valid keypress.
+
+
+    return true;
+  }
+
+  function keyToModifier(key) {
+    switch (key) {
+      case '/':
+        return 'slash';
+
+      case ' ':
+      case 'Spacebar':
+        return 'space';
+
+      default:
+        return key && kebabCase(key);
+    }
+  }
+
+  function registerModelListener(component, el, modifiers, expression, extraVars) {
+    // If the element we are binding to is a select, a radio, or checkbox
+    // we'll listen for the change event instead of the "input" event.
+    var event = el.tagName.toLowerCase() === 'select' || ['checkbox', 'radio'].includes(el.type) || modifiers.includes('lazy') ? 'change' : 'input';
+    const listenerExpression = `${expression} = rightSideOfExpression($event, ${expression})`;
+    registerListener(component, el, event, modifiers, listenerExpression, () => {
+      return _objectSpread2(_objectSpread2({}, extraVars()), {}, {
+        rightSideOfExpression: generateModelAssignmentFunction(el, modifiers, expression)
+      });
+    });
+  }
+
+  function generateModelAssignmentFunction(el, modifiers, expression) {
+    if (el.type === 'radio') {
+      // Radio buttons only work properly when they share a name attribute.
+      // People might assume we take care of that for them, because
+      // they already set a shared "x-model" attribute.
+      if (!el.hasAttribute('name')) el.setAttribute('name', expression);
+    }
+
+    return (event, currentValue) => {
+      // Check for event.detail due to an issue where IE11 handles other events as a CustomEvent.
+      if (event instanceof CustomEvent && event.detail) {
+        return event.detail;
+      } else if (el.type === 'checkbox') {
+        // If the data we are binding to is an array, toggle its value inside the array.
+        if (Array.isArray(currentValue)) {
+          const newValue = modifiers.includes('number') ? safeParseNumber(event.target.value) : event.target.value;
+          return event.target.checked ? currentValue.concat([newValue]) : currentValue.filter(el => !checkedAttrLooseCompare(el, newValue));
+        } else {
+          return event.target.checked;
+        }
+      } else if (el.tagName.toLowerCase() === 'select' && el.multiple) {
+        return modifiers.includes('number') ? Array.from(event.target.selectedOptions).map(option => {
+          const rawValue = option.value || option.text;
+          return safeParseNumber(rawValue);
+        }) : Array.from(event.target.selectedOptions).map(option => {
+          return option.value || option.text;
+        });
+      } else {
+        const rawValue = event.target.value;
+        return modifiers.includes('number') ? safeParseNumber(rawValue) : modifiers.includes('trim') ? rawValue.trim() : rawValue;
+      }
+    };
+  }
+
+  function safeParseNumber(rawValue) {
+    const number = rawValue ? parseFloat(rawValue) : null;
+    return isNumeric(number) ? number : rawValue;
+  }
+
+  /**
+   * Copyright (C) 2017 salesforce.com, inc.
+   */
+  const { isArray } = Array;
+  const { getPrototypeOf, create: ObjectCreate, defineProperty: ObjectDefineProperty, defineProperties: ObjectDefineProperties, isExtensible, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols, preventExtensions, hasOwnProperty, } = Object;
+  const { push: ArrayPush, concat: ArrayConcat, map: ArrayMap, } = Array.prototype;
+  function isUndefined(obj) {
+      return obj === undefined;
+  }
+  function isFunction(obj) {
+      return typeof obj === 'function';
+  }
+  function isObject(obj) {
+      return typeof obj === 'object';
+  }
+  const proxyToValueMap = new WeakMap();
+  function registerProxy(proxy, value) {
+      proxyToValueMap.set(proxy, value);
+  }
+  const unwrap$1 = (replicaOrAny) => proxyToValueMap.get(replicaOrAny) || replicaOrAny;
+
+  function wrapValue(membrane, value) {
+      return membrane.valueIsObservable(value) ? membrane.getProxy(value) : value;
+  }
+  /**
+   * Unwrap property descriptors will set value on original descriptor
+   * We only need to unwrap if value is specified
+   * @param descriptor external descrpitor provided to define new property on original value
+   */
+  function unwrapDescriptor(descriptor) {
+      if (hasOwnProperty.call(descriptor, 'value')) {
+          descriptor.value = unwrap$1(descriptor.value);
+      }
+      return descriptor;
+  }
+  function lockShadowTarget(membrane, shadowTarget, originalTarget) {
+      const targetKeys = ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
+      targetKeys.forEach((key) => {
+          let descriptor = getOwnPropertyDescriptor(originalTarget, key);
+          // We do not need to wrap the descriptor if configurable
+          // Because we can deal with wrapping it when user goes through
+          // Get own property descriptor. There is also a chance that this descriptor
+          // could change sometime in the future, so we can defer wrapping
+          // until we need to
+          if (!descriptor.configurable) {
+              descriptor = wrapDescriptor(membrane, descriptor, wrapValue);
+          }
+          ObjectDefineProperty(shadowTarget, key, descriptor);
+      });
+      preventExtensions(shadowTarget);
+  }
+  class ReactiveProxyHandler {
+      constructor(membrane, value) {
+          this.originalTarget = value;
+          this.membrane = membrane;
+      }
+      get(shadowTarget, key) {
+          const { originalTarget, membrane } = this;
+          const value = originalTarget[key];
+          const { valueObserved } = membrane;
+          valueObserved(originalTarget, key);
+          return membrane.getProxy(value);
+      }
+      set(shadowTarget, key, value) {
+          const { originalTarget, membrane: { valueMutated } } = this;
+          const oldValue = originalTarget[key];
+          if (oldValue !== value) {
+              originalTarget[key] = value;
+              valueMutated(originalTarget, key);
+          }
+          else if (key === 'length' && isArray(originalTarget)) {
+              // fix for issue #236: push will add the new index, and by the time length
+              // is updated, the internal length is already equal to the new length value
+              // therefore, the oldValue is equal to the value. This is the forking logic
+              // to support this use case.
+              valueMutated(originalTarget, key);
+          }
+          return true;
+      }
+      deleteProperty(shadowTarget, key) {
+          const { originalTarget, membrane: { valueMutated } } = this;
+          delete originalTarget[key];
+          valueMutated(originalTarget, key);
+          return true;
+      }
+      apply(shadowTarget, thisArg, argArray) {
+          /* No op */
+      }
+      construct(target, argArray, newTarget) {
+          /* No op */
+      }
+      has(shadowTarget, key) {
+          const { originalTarget, membrane: { valueObserved } } = this;
+          valueObserved(originalTarget, key);
+          return key in originalTarget;
+      }
+      ownKeys(shadowTarget) {
+          const { originalTarget } = this;
+          return ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
+      }
+      isExtensible(shadowTarget) {
+          const shadowIsExtensible = isExtensible(shadowTarget);
+          if (!shadowIsExtensible) {
+              return shadowIsExtensible;
+          }
+          const { originalTarget, membrane } = this;
+          const targetIsExtensible = isExtensible(originalTarget);
+          if (!targetIsExtensible) {
+              lockShadowTarget(membrane, shadowTarget, originalTarget);
+          }
+          return targetIsExtensible;
+      }
+      setPrototypeOf(shadowTarget, prototype) {
+      }
+      getPrototypeOf(shadowTarget) {
+          const { originalTarget } = this;
+          return getPrototypeOf(originalTarget);
+      }
+      getOwnPropertyDescriptor(shadowTarget, key) {
+          const { originalTarget, membrane } = this;
+          const { valueObserved } = this.membrane;
+          // keys looked up via hasOwnProperty need to be reactive
+          valueObserved(originalTarget, key);
+          let desc = getOwnPropertyDescriptor(originalTarget, key);
+          if (isUndefined(desc)) {
+              return desc;
+          }
+          const shadowDescriptor = getOwnPropertyDescriptor(shadowTarget, key);
+          if (!isUndefined(shadowDescriptor)) {
+              return shadowDescriptor;
+          }
+          // Note: by accessing the descriptor, the key is marked as observed
+          // but access to the value, setter or getter (if available) cannot observe
+          // mutations, just like regular methods, in which case we just do nothing.
+          desc = wrapDescriptor(membrane, desc, wrapValue);
+          if (!desc.configurable) {
+              // If descriptor from original target is not configurable,
+              // We must copy the wrapped descriptor over to the shadow target.
+              // Otherwise, proxy will throw an invariant error.
+              // This is our last chance to lock the value.
+              // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Invariants
+              ObjectDefineProperty(shadowTarget, key, desc);
+          }
+          return desc;
+      }
+      preventExtensions(shadowTarget) {
+          const { originalTarget, membrane } = this;
+          lockShadowTarget(membrane, shadowTarget, originalTarget);
+          preventExtensions(originalTarget);
+          return true;
+      }
+      defineProperty(shadowTarget, key, descriptor) {
+          const { originalTarget, membrane } = this;
+          const { valueMutated } = membrane;
+          const { configurable } = descriptor;
+          // We have to check for value in descriptor
+          // because Object.freeze(proxy) calls this method
+          // with only { configurable: false, writeable: false }
+          // Additionally, method will only be called with writeable:false
+          // if the descriptor has a value, as opposed to getter/setter
+          // So we can just check if writable is present and then see if
+          // value is present. This eliminates getter and setter descriptors
+          if (hasOwnProperty.call(descriptor, 'writable') && !hasOwnProperty.call(descriptor, 'value')) {
+              const originalDescriptor = getOwnPropertyDescriptor(originalTarget, key);
+              descriptor.value = originalDescriptor.value;
+          }
+          ObjectDefineProperty(originalTarget, key, unwrapDescriptor(descriptor));
+          if (configurable === false) {
+              ObjectDefineProperty(shadowTarget, key, wrapDescriptor(membrane, descriptor, wrapValue));
+          }
+          valueMutated(originalTarget, key);
+          return true;
+      }
+  }
+
+  function wrapReadOnlyValue(membrane, value) {
+      return membrane.valueIsObservable(value) ? membrane.getReadOnlyProxy(value) : value;
+  }
+  class ReadOnlyHandler {
+      constructor(membrane, value) {
+          this.originalTarget = value;
+          this.membrane = membrane;
+      }
+      get(shadowTarget, key) {
+          const { membrane, originalTarget } = this;
+          const value = originalTarget[key];
+          const { valueObserved } = membrane;
+          valueObserved(originalTarget, key);
+          return membrane.getReadOnlyProxy(value);
+      }
+      set(shadowTarget, key, value) {
+          return false;
+      }
+      deleteProperty(shadowTarget, key) {
+          return false;
+      }
+      apply(shadowTarget, thisArg, argArray) {
+          /* No op */
+      }
+      construct(target, argArray, newTarget) {
+          /* No op */
+      }
+      has(shadowTarget, key) {
+          const { originalTarget, membrane: { valueObserved } } = this;
+          valueObserved(originalTarget, key);
+          return key in originalTarget;
+      }
+      ownKeys(shadowTarget) {
+          const { originalTarget } = this;
+          return ArrayConcat.call(getOwnPropertyNames(originalTarget), getOwnPropertySymbols(originalTarget));
+      }
+      setPrototypeOf(shadowTarget, prototype) {
+      }
+      getOwnPropertyDescriptor(shadowTarget, key) {
+          const { originalTarget, membrane } = this;
+          const { valueObserved } = membrane;
+          // keys looked up via hasOwnProperty need to be reactive
+          valueObserved(originalTarget, key);
+          let desc = getOwnPropertyDescriptor(originalTarget, key);
+          if (isUndefined(desc)) {
+              return desc;
+          }
+          const shadowDescriptor = getOwnPropertyDescriptor(shadowTarget, key);
+          if (!isUndefined(shadowDescriptor)) {
+              return shadowDescriptor;
+          }
+          // Note: by accessing the descriptor, the key is marked as observed
+          // but access to the value or getter (if available) cannot be observed,
+          // just like regular methods, in which case we just do nothing.
+          desc = wrapDescriptor(membrane, desc, wrapReadOnlyValue);
+          if (hasOwnProperty.call(desc, 'set')) {
+              desc.set = undefined; // readOnly membrane does not allow setters
+          }
+          if (!desc.configurable) {
+              // If descriptor from original target is not configurable,
+              // We must copy the wrapped descriptor over to the shadow target.
+              // Otherwise, proxy will throw an invariant error.
+              // This is our last chance to lock the value.
+              // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor#Invariants
+              ObjectDefineProperty(shadowTarget, key, desc);
+          }
+          return desc;
+      }
+      preventExtensions(shadowTarget) {
+          return false;
+      }
+      defineProperty(shadowTarget, key, descriptor) {
+          return false;
+      }
+  }
+  function createShadowTarget(value) {
+      let shadowTarget = undefined;
+      if (isArray(value)) {
+          shadowTarget = [];
+      }
+      else if (isObject(value)) {
+          shadowTarget = {};
+      }
+      return shadowTarget;
+  }
+  const ObjectDotPrototype = Object.prototype;
+  function defaultValueIsObservable(value) {
+      // intentionally checking for null
+      if (value === null) {
+          return false;
+      }
+      // treat all non-object types, including undefined, as non-observable values
+      if (typeof value !== 'object') {
+          return false;
+      }
+      if (isArray(value)) {
+          return true;
+      }
+      const proto = getPrototypeOf(value);
+      return (proto === ObjectDotPrototype || proto === null || getPrototypeOf(proto) === null);
+  }
+  const defaultValueObserved = (obj, key) => {
+      /* do nothing */
+  };
+  const defaultValueMutated = (obj, key) => {
+      /* do nothing */
+  };
+  const defaultValueDistortion = (value) => value;
+  function wrapDescriptor(membrane, descriptor, getValue) {
+      const { set, get } = descriptor;
+      if (hasOwnProperty.call(descriptor, 'value')) {
+          descriptor.value = getValue(membrane, descriptor.value);
+      }
+      else {
+          if (!isUndefined(get)) {
+              descriptor.get = function () {
+                  // invoking the original getter with the original target
+                  return getValue(membrane, get.call(unwrap$1(this)));
+              };
+          }
+          if (!isUndefined(set)) {
+              descriptor.set = function (value) {
+                  // At this point we don't have a clear indication of whether
+                  // or not a valid mutation will occur, we don't have the key,
+                  // and we are not sure why and how they are invoking this setter.
+                  // Nevertheless we preserve the original semantics by invoking the
+                  // original setter with the original target and the unwrapped value
+                  set.call(unwrap$1(this), membrane.unwrapProxy(value));
+              };
+          }
+      }
+      return descriptor;
+  }
+  class ReactiveMembrane {
+      constructor(options) {
+          this.valueDistortion = defaultValueDistortion;
+          this.valueMutated = defaultValueMutated;
+          this.valueObserved = defaultValueObserved;
+          this.valueIsObservable = defaultValueIsObservable;
+          this.objectGraph = new WeakMap();
+          if (!isUndefined(options)) {
+              const { valueDistortion, valueMutated, valueObserved, valueIsObservable } = options;
+              this.valueDistortion = isFunction(valueDistortion) ? valueDistortion : defaultValueDistortion;
+              this.valueMutated = isFunction(valueMutated) ? valueMutated : defaultValueMutated;
+              this.valueObserved = isFunction(valueObserved) ? valueObserved : defaultValueObserved;
+              this.valueIsObservable = isFunction(valueIsObservable) ? valueIsObservable : defaultValueIsObservable;
+          }
+      }
+      getProxy(value) {
+          const unwrappedValue = unwrap$1(value);
+          const distorted = this.valueDistortion(unwrappedValue);
+          if (this.valueIsObservable(distorted)) {
+              const o = this.getReactiveState(unwrappedValue, distorted);
+              // when trying to extract the writable version of a readonly
+              // we return the readonly.
+              return o.readOnly === value ? value : o.reactive;
+          }
+          return distorted;
+      }
+      getReadOnlyProxy(value) {
+          value = unwrap$1(value);
+          const distorted = this.valueDistortion(value);
+          if (this.valueIsObservable(distorted)) {
+              return this.getReactiveState(value, distorted).readOnly;
+          }
+          return distorted;
+      }
+      unwrapProxy(p) {
+          return unwrap$1(p);
+      }
+      getReactiveState(value, distortedValue) {
+          const { objectGraph, } = this;
+          let reactiveState = objectGraph.get(distortedValue);
+          if (reactiveState) {
+              return reactiveState;
+          }
+          const membrane = this;
+          reactiveState = {
+              get reactive() {
+                  const reactiveHandler = new ReactiveProxyHandler(membrane, distortedValue);
+                  // caching the reactive proxy after the first time it is accessed
+                  const proxy = new Proxy(createShadowTarget(distortedValue), reactiveHandler);
+                  registerProxy(proxy, value);
+                  ObjectDefineProperty(this, 'reactive', { value: proxy });
+                  return proxy;
+              },
+              get readOnly() {
+                  const readOnlyHandler = new ReadOnlyHandler(membrane, distortedValue);
+                  // caching the readOnly proxy after the first time it is accessed
+                  const proxy = new Proxy(createShadowTarget(distortedValue), readOnlyHandler);
+                  registerProxy(proxy, value);
+                  ObjectDefineProperty(this, 'readOnly', { value: proxy });
+                  return proxy;
+              }
+          };
+          objectGraph.set(distortedValue, reactiveState);
+          return reactiveState;
+      }
+  }
+  /** version: 0.26.0 */
+
+  function wrap(data, mutationCallback) {
+
+    let membrane = new ReactiveMembrane({
+      valueMutated(target, key) {
+        mutationCallback(target, key);
+      }
+
+    });
+    return {
+      data: membrane.getProxy(data),
+      membrane: membrane
+    };
+  }
+  function unwrap(membrane, observable) {
+    let unwrappedData = membrane.unwrapProxy(observable);
+    let copy = {};
+    Object.keys(unwrappedData).forEach(key => {
+      if (['$el', '$refs', '$nextTick', '$watch'].includes(key)) return;
+      copy[key] = unwrappedData[key];
+    });
+    return copy;
+  }
+
+  class Component {
+    constructor(el, componentForClone = null) {
+      this.$el = el;
+      const dataAttr = this.$el.getAttribute('x-data');
+      const dataExpression = dataAttr === '' ? '{}' : dataAttr;
+      let dataExtras = {
+        $el: this.$el
+      };
+      let canonicalComponentElementReference = componentForClone ? componentForClone.$el : this.$el;
+      Object.entries(Alpine.magicProperties).forEach(([name, callback]) => {
+        Object.defineProperty(dataExtras, `$${name}`, {
+          get: function get() {
+            return callback(canonicalComponentElementReference);
+          }
+        });
+      });
+      this.unobservedData = componentForClone ? componentForClone.getUnobservedData() : saferEval(el, dataExpression, dataExtras);
+      // Construct a Proxy-based observable. This will be used to handle reactivity.
+
+      let {
+        membrane,
+        data
+      } = this.wrapDataInObservable(this.unobservedData);
+      this.$data = data;
+      this.membrane = membrane; // After making user-supplied data methods reactive, we can now add
+      // our magic properties to the original data for access.
+
+      this.unobservedData.$el = this.$el;
+      this.unobservedData.$refs = this.getRefsProxy();
+      this.nextTickStack = [];
+
+      this.unobservedData.$nextTick = callback => {
+        this.nextTickStack.push(callback);
+      };
+
+      this.watchers = {};
+
+      this.unobservedData.$watch = (property, callback) => {
+        if (!this.watchers[property]) this.watchers[property] = [];
+        this.watchers[property].push(callback);
+      };
+      /* MODERN-ONLY:START */
+      // We remove this piece of code from the legacy build.
+      // In IE11, we have already defined our helpers at this point.
+      // Register custom magic properties.
+
+
+      Object.entries(Alpine.magicProperties).forEach(([name, callback]) => {
+        Object.defineProperty(this.unobservedData, `$${name}`, {
+          get: function get() {
+            return callback(canonicalComponentElementReference, this.$el);
+          }
+        });
+      });
+      /* MODERN-ONLY:END */
+
+      this.showDirectiveStack = [];
+      this.showDirectiveLastElement;
+      componentForClone || Alpine.onBeforeComponentInitializeds.forEach(callback => callback(this));
+      const initExpression = this.$el.getAttribute('x-init');
+      var initReturnedCallback; // If x-init is present AND we aren't cloning (skip x-init on clone)
+
+      if (initExpression && !componentForClone) {
+        // We want to allow data manipulation, but not trigger DOM updates just yet.
+        // We haven't even initialized the elements with their Alpine bindings. I mean c'mon.
+        this.pauseReactivity = true;
+        initReturnedCallback = this.evaluateReturnExpression(this.$el, initExpression);
+        this.pauseReactivity = false;
+      } // Register all our listeners and set all our attribute bindings.
+      // If we're cloning a component, the third parameter ensures no duplicate
+      // event listeners are registered (the mutation observer will take care of them)
+
+
+      this.initializeElements(this.$el, () => {}, componentForClone); // Use mutation observer to detect new elements being added within this component at run-time.
+      // Alpine's just so darn flexible amirite?
+
+      this.listenForNewElementsToInitialize();
+
+      if (typeof initReturnedCallback === 'function') {
+        // Run the callback returned from the "x-init" hook to allow the user to do stuff after
+        // Alpine's got it's grubby little paws all over everything.
+        initReturnedCallback.call(this.$data);
+      }
+
+      componentForClone || setTimeout(() => {
+        Alpine.onComponentInitializeds.forEach(callback => callback(this));
+      }, 0);
+    }
+
+    getUnobservedData() {
+      return unwrap(this.membrane, this.$data);
+    }
+
+    wrapDataInObservable(data) {
+      var self = this;
+      let updateDom = debounce(function () {
+        self.updateElements(self.$el);
+      }, 0);
+      return wrap(data, (target, key) => {
+        if (self.watchers[key]) {
+          // If there's a watcher for this specific key, run it.
+          self.watchers[key].forEach(callback => callback(target[key]));
+        } else if (Array.isArray(target)) {
+          // Arrays are special cases, if any of the items change, we consider the array as mutated.
+          Object.keys(self.watchers).forEach(fullDotNotationKey => {
+            let dotNotationParts = fullDotNotationKey.split('.'); // Ignore length mutations since they would result in duplicate calls.
+            // For example, when calling push, we would get a mutation for the item's key
+            // and a second mutation for the length property.
+
+            if (key === 'length') return;
+            dotNotationParts.reduce((comparisonData, part) => {
+              if (Object.is(target, comparisonData[part])) {
+                self.watchers[fullDotNotationKey].forEach(callback => callback(target));
+              }
+
+              return comparisonData[part];
+            }, self.unobservedData);
+          });
+        } else {
+          // Let's walk through the watchers with "dot-notation" (foo.bar) and see
+          // if this mutation fits any of them.
+          Object.keys(self.watchers).filter(i => i.includes('.')).forEach(fullDotNotationKey => {
+            let dotNotationParts = fullDotNotationKey.split('.'); // If this dot-notation watcher's last "part" doesn't match the current
+            // key, then skip it early for performance reasons.
+
+            if (key !== dotNotationParts[dotNotationParts.length - 1]) return; // Now, walk through the dot-notation "parts" recursively to find
+            // a match, and call the watcher if one's found.
+
+            dotNotationParts.reduce((comparisonData, part) => {
+              if (Object.is(target, comparisonData)) {
+                // Run the watchers.
+                self.watchers[fullDotNotationKey].forEach(callback => callback(target[key]));
+              }
+
+              return comparisonData[part];
+            }, self.unobservedData);
+          });
+        } // Don't react to data changes for cases like the `x-created` hook.
+
+
+        if (self.pauseReactivity) return;
+        updateDom();
+      });
+    }
+
+    walkAndSkipNestedComponents(el, callback, initializeComponentCallback = () => {}) {
+      walk(el, el => {
+        // We've hit a component.
+        if (el.hasAttribute('x-data')) {
+          // If it's not the current one.
+          if (!el.isSameNode(this.$el)) {
+            // Initialize it if it's not.
+            if (!el.__x) initializeComponentCallback(el); // Now we'll let that sub-component deal with itself.
+
+            return false;
+          }
+        }
+
+        return callback(el);
+      });
+    }
+
+    initializeElements(rootEl, extraVars = () => {}, componentForClone = false) {
+      this.walkAndSkipNestedComponents(rootEl, el => {
+        // Don't touch spawns from for loop
+        if (el.__x_for_key !== undefined) return false; // Don't touch spawns from if directives
+
+        if (el.__x_inserted_me !== undefined) return false;
+        this.initializeElement(el, extraVars, componentForClone ? false : true);
+      }, el => {
+        if (!componentForClone) el.__x = new Component(el);
+      });
+      this.executeAndClearRemainingShowDirectiveStack();
+      this.executeAndClearNextTickStack(rootEl);
+    }
+
+    initializeElement(el, extraVars, shouldRegisterListeners = true) {
+      // To support class attribute merging, we have to know what the element's
+      // original class attribute looked like for reference.
+      if (el.hasAttribute('class') && getXAttrs(el, this).length > 0) {
+        el.__x_original_classes = convertClassStringToArray(el.getAttribute('class'));
+      }
+
+      shouldRegisterListeners && this.registerListeners(el, extraVars);
+      this.resolveBoundAttributes(el, true, extraVars);
+    }
+
+    updateElements(rootEl, extraVars = () => {}) {
+      this.walkAndSkipNestedComponents(rootEl, el => {
+        // Don't touch spawns from for loop (and check if the root is actually a for loop in a parent, don't skip it.)
+        if (el.__x_for_key !== undefined && !el.isSameNode(this.$el)) return false;
+        this.updateElement(el, extraVars);
+      }, el => {
+        el.__x = new Component(el);
+      });
+      this.executeAndClearRemainingShowDirectiveStack();
+      this.executeAndClearNextTickStack(rootEl);
+    }
+
+    executeAndClearNextTickStack(el) {
+      // Skip spawns from alpine directives
+      if (el === this.$el && this.nextTickStack.length > 0) {
+        // We run the tick stack after the next frame to allow any
+        // running transitions to pass the initial show stage.
+        requestAnimationFrame(() => {
+          while (this.nextTickStack.length > 0) {
+            this.nextTickStack.shift()();
+          }
+        });
+      }
+    }
+
+    executeAndClearRemainingShowDirectiveStack() {
+      // The goal here is to start all the x-show transitions
+      // and build a nested promise chain so that elements
+      // only hide when the children are finished hiding.
+      this.showDirectiveStack.reverse().map(handler => {
+        return new Promise((resolve, reject) => {
+          handler(resolve, reject);
+        });
+      }).reduce((promiseChain, promise) => {
+        return promiseChain.then(() => {
+          return promise.then(finishElement => {
+            finishElement();
+          });
+        });
+      }, Promise.resolve(() => {})).catch(e => {
+        if (e !== TRANSITION_CANCELLED) throw e;
+      }); // We've processed the handler stack. let's clear it.
+
+      this.showDirectiveStack = [];
+      this.showDirectiveLastElement = undefined;
+    }
+
+    updateElement(el, extraVars) {
+      this.resolveBoundAttributes(el, false, extraVars);
+    }
+
+    registerListeners(el, extraVars) {
+      getXAttrs(el, this).forEach(({
+        type,
+        value,
+        modifiers,
+        expression
+      }) => {
+        switch (type) {
+          case 'on':
+            registerListener(this, el, value, modifiers, expression, extraVars);
+            break;
+
+          case 'model':
+            registerModelListener(this, el, modifiers, expression, extraVars);
+            break;
+        }
+      });
+    }
+
+    resolveBoundAttributes(el, initialUpdate = false, extraVars) {
+      let attrs = getXAttrs(el, this);
+      attrs.forEach(({
+        type,
+        value,
+        modifiers,
+        expression
+      }) => {
+        switch (type) {
+          case 'model':
+            handleAttributeBindingDirective(this, el, 'value', expression, extraVars, type, modifiers);
+            break;
+
+          case 'bind':
+            // The :key binding on an x-for is special, ignore it.
+            if (el.tagName.toLowerCase() === 'template' && value === 'key') return;
+            handleAttributeBindingDirective(this, el, value, expression, extraVars, type, modifiers);
+            break;
+
+          case 'text':
+            var output = this.evaluateReturnExpression(el, expression, extraVars);
+            handleTextDirective(el, output, expression);
+            break;
+
+          case 'html':
+            handleHtmlDirective(this, el, expression, extraVars);
+            break;
+
+          case 'show':
+            var output = this.evaluateReturnExpression(el, expression, extraVars);
+            handleShowDirective(this, el, output, modifiers, initialUpdate);
+            break;
+
+          case 'if':
+            // If this element also has x-for on it, don't process x-if.
+            // We will let the "x-for" directive handle the "if"ing.
+            if (attrs.some(i => i.type === 'for')) return;
+            var output = this.evaluateReturnExpression(el, expression, extraVars);
+            handleIfDirective(this, el, output, initialUpdate, extraVars);
+            break;
+
+          case 'for':
+            handleForDirective(this, el, expression, initialUpdate, extraVars);
+            break;
+
+          case 'cloak':
+            el.removeAttribute('x-cloak');
+            break;
+        }
+      });
+    }
+
+    evaluateReturnExpression(el, expression, extraVars = () => {}) {
+      return saferEval(el, expression, this.$data, _objectSpread2(_objectSpread2({}, extraVars()), {}, {
+        $dispatch: this.getDispatchFunction(el)
+      }));
+    }
+
+    evaluateCommandExpression(el, expression, extraVars = () => {}) {
+      return saferEvalNoReturn(el, expression, this.$data, _objectSpread2(_objectSpread2({}, extraVars()), {}, {
+        $dispatch: this.getDispatchFunction(el)
+      }));
+    }
+
+    getDispatchFunction(el) {
+      return (event, detail = {}) => {
+        el.dispatchEvent(new CustomEvent(event, {
+          detail,
+          bubbles: true
+        }));
+      };
+    }
+
+    listenForNewElementsToInitialize() {
+      const targetNode = this.$el;
+      const observerOptions = {
+        childList: true,
+        attributes: true,
+        subtree: true
+      };
+      const observer = new MutationObserver(mutations => {
+        for (let i = 0; i < mutations.length; i++) {
+          // Filter out mutations triggered from child components.
+          const closestParentComponent = mutations[i].target.closest('[x-data]');
+          if (!(closestParentComponent && closestParentComponent.isSameNode(this.$el))) continue;
+
+          if (mutations[i].type === 'attributes' && mutations[i].attributeName === 'x-data') {
+            const xAttr = mutations[i].target.getAttribute('x-data') || '{}';
+            const rawData = saferEval(this.$el, xAttr, {
+              $el: this.$el
+            });
+            Object.keys(rawData).forEach(key => {
+              if (this.$data[key] !== rawData[key]) {
+                this.$data[key] = rawData[key];
+              }
+            });
+          }
+
+          if (mutations[i].addedNodes.length > 0) {
+            mutations[i].addedNodes.forEach(node => {
+              if (node.nodeType !== 1 || node.__x_inserted_me) return;
+
+              if (node.matches('[x-data]') && !node.__x) {
+                node.__x = new Component(node);
+                return;
+              }
+
+              node.__x_node_add_count = (node.__x_node_add_count || 0) + 1;
+              this.initializeElements(node);
+            });
+          }
+        }
+      });
+      observer.observe(targetNode, observerOptions);
+    }
+
+    getRefsProxy() {
+      var self = this;
+      var refObj = {};
+      // One of the goals of this is to not hold elements in memory, but rather re-evaluate
+      // the DOM when the system needs something from it. This way, the framework is flexible and
+      // friendly to outside DOM changes from libraries like Vue/Livewire.
+      // For this reason, I'm using an "on-demand" proxy to fake a "$refs" object.
+
+      return new Proxy(refObj, {
+        get(object, property) {
+          if (property === '$isAlpineProxy') return true;
+          var ref; // We can't just query the DOM because it's hard to filter out refs in
+          // nested components.
+
+          self.walkAndSkipNestedComponents(self.$el, el => {
+            if (el.hasAttribute('x-ref') && el.getAttribute('x-ref') === property) {
+              ref = el;
+            }
+          });
+          return ref;
+        }
+
+      });
+    }
+
+  }
+
+  const Alpine = {
+    version: "2.8.2",
+    pauseMutationObserver: false,
+    magicProperties: {},
+    onComponentInitializeds: [],
+    onBeforeComponentInitializeds: [],
+    ignoreFocusedForValueBinding: false,
+    start: async function start() {
+      if (!isTesting()) {
+        await domReady();
+      }
+
+      this.discoverComponents(el => {
+        this.initializeComponent(el);
+      }); // It's easier and more performant to just support Turbolinks than listen
+      // to MutationObserver mutations at the document level.
+
+      document.addEventListener("turbolinks:load", () => {
+        this.discoverUninitializedComponents(el => {
+          this.initializeComponent(el);
+        });
+      });
+      this.listenForNewUninitializedComponentsAtRunTime();
+    },
+    discoverComponents: function discoverComponents(callback) {
+      const rootEls = document.querySelectorAll('[x-data]');
+      rootEls.forEach(rootEl => {
+        callback(rootEl);
+      });
+    },
+    discoverUninitializedComponents: function discoverUninitializedComponents(callback, el = null) {
+      const rootEls = (el || document).querySelectorAll('[x-data]');
+      Array.from(rootEls).filter(el => el.__x === undefined).forEach(rootEl => {
+        callback(rootEl);
+      });
+    },
+    listenForNewUninitializedComponentsAtRunTime: function listenForNewUninitializedComponentsAtRunTime() {
+      const targetNode = document.querySelector('body');
+      const observerOptions = {
+        childList: true,
+        attributes: true,
+        subtree: true
+      };
+      const observer = new MutationObserver(mutations => {
+        if (this.pauseMutationObserver) return;
+
+        for (let i = 0; i < mutations.length; i++) {
+          if (mutations[i].addedNodes.length > 0) {
+            mutations[i].addedNodes.forEach(node => {
+              // Discard non-element nodes (like line-breaks)
+              if (node.nodeType !== 1) return; // Discard any changes happening within an existing component.
+              // They will take care of themselves.
+
+              if (node.parentElement && node.parentElement.closest('[x-data]')) return;
+              this.discoverUninitializedComponents(el => {
+                this.initializeComponent(el);
+              }, node.parentElement);
+            });
+          }
+        }
+      });
+      observer.observe(targetNode, observerOptions);
+    },
+    initializeComponent: function initializeComponent(el) {
+      if (!el.__x) {
+        // Wrap in a try/catch so that we don't prevent other components
+        // from initializing when one component contains an error.
+        try {
+          el.__x = new Component(el);
+        } catch (error) {
+          setTimeout(() => {
+            throw error;
+          }, 0);
+        }
+      }
+    },
+    clone: function clone(component, newEl) {
+      if (!newEl.__x) {
+        newEl.__x = new Component(newEl, component);
+      }
+    },
+    addMagicProperty: function addMagicProperty(name, callback) {
+      this.magicProperties[name] = callback;
+    },
+    onComponentInitialized: function onComponentInitialized(callback) {
+      this.onComponentInitializeds.push(callback);
+    },
+    onBeforeComponentInitialized: function onBeforeComponentInitialized(callback) {
+      this.onBeforeComponentInitializeds.push(callback);
+    }
+  };
+
+  if (!isTesting()) {
+    window.Alpine = Alpine;
+
+    if (window.deferLoadingAlpine) {
+      window.deferLoadingAlpine(function () {
+        window.Alpine.start();
+      });
+    } else {
+      window.Alpine.start();
+    }
+  }
+
+  return Alpine;
+
+})));

+ 67 - 0
public/assets/typhoon/js/appearance.js

@@ -0,0 +1,67 @@
+((function(global) {
+  const DATA = JSON.parse(document.documentElement.dataset.appearance || 'null');
+  const matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
+
+  try {
+    // Chrome & Firefox
+    matchMedia.addEventListener('change', (event) => {
+      if (typhoonRetrieve().appearance === 'system') {
+        typhoonSetTheme(event.matches ? 'dark' : 'light', true);
+      }
+    });
+  } catch (fallback) {
+    try {
+      // Safari
+      matchMedia.addListener((event) => {
+        if (typhoonRetrieve().appearance === 'system') {
+          typhoonSetTheme(event.matches ? 'dark' : 'light', true);
+        }
+      });
+    } catch (error) {
+      console.error(error);
+    }
+  }
+
+  global.typhoonStore = function(data) {
+    const STORAGE = DATA.store ? 'localStorage' : 'sessionStorage';
+    const theme = data.appearance === 'system' ? typhoonGetTheme() : data.appearance;
+    const config = Object.assign({}, typhoonRetrieve(), data, { theme: theme });
+    global[STORAGE].setItem('typhoon-appearance', JSON.stringify(config));
+    typhoonSetTheme(theme, false);
+  }
+
+  global.typhoonRetrieve = function() {
+    const STORAGE = DATA.store ? 'localStorage' : 'sessionStorage';
+    const systemTheme = typhoonGetTheme();
+    const storage = JSON.parse(global[STORAGE].getItem('typhoon-appearance') || 'null');
+
+    if (storage && storage.appearance === 'system') {
+      storage.theme = systemTheme;
+    }
+
+    return storage || {
+      theme: DATA.appearance === 'system' ? systemTheme : DATA.appearance,
+      appearance: DATA.appearance || 'system'
+    };
+  }
+
+  global.typhoonSetTheme = function(theme, store) {
+    const event = new CustomEvent('typhoon-theme', {
+      detail: {
+        theme: theme,
+        appearance: typhoonRetrieve().appearance
+      }
+    });
+
+    if (store) {
+      typhoonStore({ theme: theme || typhoonGetTheme() });
+    }
+
+    window.dispatchEvent(event);
+  }
+
+  global.typhoonGetTheme = function() {
+    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+  }
+
+})(window));

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/assets/typhoon/js/glightbox.min.js


+ 1 - 1
resources/views/ananke.blade.php

@@ -73,7 +73,7 @@
                 <li class="list f5 f4-ns fw4 dib pr3">
                   <a
                     class="hover-white no-underline white-90"
-                    href="/my/{{ $item['link'] }}"
+                    href="/pcd/{{ $item['link'] }}"
                     title="{{ $item['title'] }}"
                   >
                     {{ $item['title'] }}

+ 816 - 0
resources/views/typhoon.blade.php

@@ -0,0 +1,816 @@
+
+<!DOCTYPE html>
+<html lang="en" data-appearance="{&quot;appearance&quot;:&quot;system&quot;,&quot;store&quot;:1}" x-data="{ show_mobile_nav: false, theme: typhoonRetrieve().theme, appearance: typhoonRetrieve().appearance }" :class="[ show_mobile_nav ? 'overflow-hidden' : '', theme ]" class="overflow-x-hidden">
+<head>
+<meta charset="utf-8" />
+<title>Modular Page | Grav</title>
+<meta http-equiv="X-UA-Compatible" content="IE=edge" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<meta name="generator" content="GravCMS" />
+<meta name="description" content="Grav is an easy to use, yet powerful, open source flat-file CMS" />
+<link rel="icon" type="image/png" href="/assets/typhoon/images/favicon.png" />
+<link rel="canonical" href="https://demo.getgrav.org/typhoon/onepage/" />
+<link href="/assets/typhoon/css/notices.css" type="text/css" rel="stylesheet">
+<link href="/assets/typhoon/css/glightbox.min.css" type="text/css" rel="stylesheet">
+<link href="/assets/typhoon/css/site.css" type="text/css" rel="stylesheet">
+<link href="/assets/typhoon/css/form-styles.css" type="text/css" rel="stylesheet">
+<style>
+:root {
+  --color-primary: #FF4C41;
+  --color-primary__lighter: #ffaca7;
+  --color-primary__darker: #da0d00;
+}
+</style>
+<script src="/assets/typhoon/js/alpine.js" defer></script>
+</head>
+<body id="top" class="flex flex-col items-stretch min-h-screen antialiased relative bg-white dark:bg-gray-900 overflow-x-hidden text-gray-600 dark:text-gray-400 " @typhoon-theme.window="theme = $event.detail.theme || ''; appearance = $event.detail.appearance || '';">
+<div class="relative bg-orange-300">
+<div class="max-w-screen-xl mx-auto py-3 px-3 sm:px-6 lg:px-8" style='display:none'>
+notification
+</div>
+</div>
+<div class="flex-1 flex flex-col relative">
+<header class="absolute w-full z-10 pb-1 h-16 flex items-center " style>
+<div class="flex-auto xl:container xl:mx-auto md:px-6 px-4">
+<nav class="header-nav relative flex items-center justify-between lg:justify-start animated ">
+<div class="flex items-center">
+<div class="flex items-center justify-between w-full md:w-auto">
+<a href="/pcd/community/list" aria-label="Logo" class="text-gray-200">
+<div class="site-logo h-8">
+
+<svg id="wikipali_banner" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 221 54.417">
+  <g id="Group_12" data-name="Group 12" transform="translate(-396 -320)">
+    <g id="Group_2" data-name="Group 2" transform="translate(396 320)">
+      <g id="Group_1" data-name="Group 1" transform="translate(39.472 12.369)">
+        <path id="Path_1" data-name="Path 1"
+          d="M252.239,132.886a1.184,1.184,0,0,1-.733-.244,1.144,1.144,0,0,1-.424-.63l-3.447-12.729a.825.825,0,0,1-.026-.206.683.683,0,0,1,.155-.411.655.655,0,0,1,.539-.257h1.234a1.129,1.129,0,0,1,.721.244,1.171,1.171,0,0,1,.411.63l1.7,6.97q.026.1.8,4.063a.046.046,0,0,0,.053.051.046.046,0,0,0,.051-.051q.925-3.96.952-4.063l1.8-6.97a1.187,1.187,0,0,1,1.132-.874h1a1.187,1.187,0,0,1,1.13.874l1.851,6.97q.153.643.475,1.993t.5,2.071a.046.046,0,0,0,.051.051.084.084,0,0,0,.078-.051q.076-.464.36-1.865t.462-2.2l1.647-6.97a1.187,1.187,0,0,1,1.132-.874h1.028a.66.66,0,0,1,.54.257.723.723,0,0,1,.155.437.813.813,0,0,1-.026.18l-3.266,12.729a1.143,1.143,0,0,1-.424.63,1.174,1.174,0,0,1-.733.244h-1.8a1.129,1.129,0,0,1-.721-.244,1.165,1.165,0,0,1-.411-.63l-1.62-6.3q-.258-1.028-.9-4.114a.1.1,0,0,0-.091-.051.091.091,0,0,0-.089.051q-.514,2.726-.9,4.142l-1.543,6.275a1.166,1.166,0,0,1-.411.63,1.12,1.12,0,0,1-.721.244h-1.671Z"
+          transform="translate(-247.61 -111.903)" fill="#fff" />
+        <path id="Path_2" data-name="Path 2"
+          d="M395.183,81.944a1.988,1.988,0,0,1-1.389.5,1.942,1.942,0,0,1-1.376-.5,1.661,1.661,0,0,1-.539-1.274,1.692,1.692,0,0,1,.539-1.3,1.94,1.94,0,0,1,1.376-.5,1.978,1.978,0,0,1,1.389.5,1.673,1.673,0,0,1,.553,1.3A1.644,1.644,0,0,1,395.183,81.944ZM393.216,99.65a.92.92,0,0,1-.926-.926V86.072a.864.864,0,0,1,.269-.63.892.892,0,0,1,.657-.269h1.157a.9.9,0,0,1,.657.269.865.865,0,0,1,.271.63V98.723a.923.923,0,0,1-.928.926Z"
+          transform="translate(-368.881 -78.666)" fill="#fff" />
+        <path id="Path_3" data-name="Path 3"
+          d="M444.3,98.574a.92.92,0,0,1-.926-.926V78.489a.869.869,0,0,1,.269-.63.892.892,0,0,1,.657-.269H445.4a.9.9,0,0,1,.657.269.863.863,0,0,1,.269.63v12.6c0,.018.013.026.038.026a.091.091,0,0,0,.065-.026l5.092-6.3a1.831,1.831,0,0,1,1.492-.693h1.518a.387.387,0,0,1,.373.244.382.382,0,0,1-.064.45l-4.269,5.092a.167.167,0,0,0,0,.18l4.937,7.74a.49.49,0,0,1,.078.257.481.481,0,0,1-.078.257.448.448,0,0,1-.437.257h-1.465a1.55,1.55,0,0,1-1.389-.772l-3.4-5.683c-.033-.069-.077-.077-.129-.026l-2.288,2.649a.336.336,0,0,0-.078.206v2.7a.92.92,0,0,1-.926.926Z"
+          transform="translate(-412.163 -77.59)" fill="#fff" />
+        <path id="Path_4" data-name="Path 4"
+          d="M540.613,81.944a1.987,1.987,0,0,1-1.388.5,1.94,1.94,0,0,1-1.376-.5,1.661,1.661,0,0,1-.539-1.274,1.692,1.692,0,0,1,.539-1.3,1.942,1.942,0,0,1,1.376-.5,1.977,1.977,0,0,1,1.388.5,1.673,1.673,0,0,1,.553,1.3A1.649,1.649,0,0,1,540.613,81.944ZM538.646,99.65a.923.923,0,0,1-.928-.926V86.072a.86.86,0,0,1,.271-.63.892.892,0,0,1,.657-.269H539.8a.9.9,0,0,1,.657.269.863.863,0,0,1,.269.63V98.723a.92.92,0,0,1-.926.926Z"
+          transform="translate(-491.128 -78.666)" fill="#fff" />
+        <path id="Path_5" data-name="Path 5"
+          d="M589.735,137.187a.92.92,0,0,1-.925-.925V117.746a.92.92,0,0,1,.925-.926h.642a1.016,1.016,0,0,1,.682.257,1.136,1.136,0,0,1,.373.642l.077.642c.018.035.038.053.064.053a.1.1,0,0,0,.065-.026,7.051,7.051,0,0,1,4.424-1.929,5,5,0,0,1,4.231,1.993,8.742,8.742,0,0,1,1.5,5.388,10.164,10.164,0,0,1-.515,3.3,6.991,6.991,0,0,1-1.389,2.469,6.473,6.473,0,0,1-1.993,1.5,5.339,5.339,0,0,1-2.353.54,5.846,5.846,0,0,1-3.729-1.569.031.031,0,0,0-.051,0,.075.075,0,0,0-.025.053l.076,2.366v3.754a.92.92,0,0,1-.926.925h-1.159Zm5.246-8.023a3.153,3.153,0,0,0,2.65-1.4,6.426,6.426,0,0,0,1.028-3.871q0-4.912-3.394-4.912a5.135,5.135,0,0,0-3.368,1.7.245.245,0,0,0-.078.18v6.866a.243.243,0,0,0,.078.18A4.734,4.734,0,0,0,594.981,129.164Z"
+          transform="translate(-534.418 -110.264)" fill="#fff" />
+        <path id="Path_6" data-name="Path 6"
+          d="M691.509,105.4a4.264,4.264,0,0,1-3.073-1.143,4.27,4.27,0,0,1,.85-6.609,16.4,16.4,0,0,1,6.518-1.787c.069,0,.1-.041.1-.129q-.1-3.032-2.751-3.034a7.157,7.157,0,0,0-3.419,1,.864.864,0,0,1-.669.089.811.811,0,0,1-.539-.424l-.257-.462a.955.955,0,0,1-.089-.706.822.822,0,0,1,.424-.553,10.423,10.423,0,0,1,5.066-1.44,4.826,4.826,0,0,1,3.96,1.594,6.988,6.988,0,0,1,1.312,4.551v7.792a.923.923,0,0,1-.928.926h-.642a1.008,1.008,0,0,1-.681-.257,1.118,1.118,0,0,1-.373-.642l-.1-.721c-.018-.033-.038-.053-.065-.053s-.046.018-.064.053A7.155,7.155,0,0,1,691.509,105.4Zm-.977-18.027a.92.92,0,0,1-.926-.926v-.206a.92.92,0,0,1,.926-.926h6.3a.92.92,0,0,1,.925.926v.206a.92.92,0,0,1-.925.926Zm1.9,15.637a5.287,5.287,0,0,0,3.4-1.6.278.278,0,0,0,.077-.206V97.9c0-.086-.033-.12-.1-.1a11.688,11.688,0,0,0-4.346,1.17,2.336,2.336,0,0,0-1.286,2.045,1.82,1.82,0,0,0,.617,1.518A2.582,2.582,0,0,0,692.435,103.015Z"
+          transform="translate(-617.157 -84.088)" fill="#fff" />
+        <path id="Path_7" data-name="Path 7"
+          d="M792.08,98.907a2.68,2.68,0,0,1-2.3-.952,4.607,4.607,0,0,1-.708-2.779V78.489a.865.865,0,0,1,.271-.63A.893.893,0,0,1,790,77.59h1.157a.9.9,0,0,1,.657.269.865.865,0,0,1,.271.63V95.333a1.12,1.12,0,0,0,.411,1.028c.034.018.11.061.231.129s.206.12.257.155.12.081.206.14a.691.691,0,0,1,.193.193.438.438,0,0,1,.064.231l.1.566a.736.736,0,0,1,.026.18.96.96,0,0,1-.155.54.792.792,0,0,1-.591.386C792.585,98.9,792.336,98.907,792.08,98.907Z"
+          transform="translate(-702.754 -77.59)" fill="#fff" />
+        <path id="Path_8" data-name="Path 8"
+          d="M840.663,81.944a1.988,1.988,0,0,1-1.389.5,1.94,1.94,0,0,1-1.376-.5,1.661,1.661,0,0,1-.539-1.274,1.692,1.692,0,0,1,.539-1.3,1.939,1.939,0,0,1,1.376-.5,1.978,1.978,0,0,1,1.389.5,1.673,1.673,0,0,1,.553,1.3A1.649,1.649,0,0,1,840.663,81.944ZM838.7,99.65a.92.92,0,0,1-.926-.926V86.072a.864.864,0,0,1,.269-.63.892.892,0,0,1,.657-.269h1.157a.9.9,0,0,1,.657.269.865.865,0,0,1,.271.63V98.723a.923.923,0,0,1-.928.926Z"
+          transform="translate(-743.346 -78.666)" fill="#fff" />
+      </g>
+      <path id="Path_9" data-name="Path 9"
+        d="M125.632,125.382a1.531,1.531,0,0,1-1.532-1.532v-5.532c0-8.629,4.134-13.579,11.342-13.579a1.532,1.532,0,0,1,0,3.064c-5.493,0-8.28,3.537-8.28,10.517v5.532A1.529,1.529,0,0,1,125.632,125.382Z"
+        transform="translate(-104.317 -88.043)" fill="#f1ca23" />
+      <path id="Path_10" data-name="Path 10"
+        d="M144.722,179.085a1.532,1.532,0,1,1,0-3.064c2.987,0,5.076-4,5.076-9.727V149.842a1.532,1.532,0,0,1,3.064,0v16.452C152.86,175.13,148.773,179.085,144.722,179.085Z"
+        transform="translate(-120.364 -124.667)" fill="#f1ca23" />
+      <path id="Path_11" data-name="Path 11"
+        d="M84.262,37.339a1.531,1.531,0,0,1-1.532-1.532V1.532a1.532,1.532,0,0,1,3.064,0V35.809A1.531,1.531,0,0,1,84.262,37.339Z"
+        transform="translate(-69.542)" fill="#f1ca23" />
+      <path id="Path_12" data-name="Path 12"
+        d="M42.892,37.339a1.531,1.531,0,0,1-1.532-1.532V1.532a1.532,1.532,0,0,1,3.064,0V35.809A1.531,1.531,0,0,1,42.892,37.339Z"
+        transform="translate(-34.767)" fill="#f1ca23" />
+      <path id="Path_13" data-name="Path 13"
+        d="M1.532,37.339A1.531,1.531,0,0,1,0,35.808V1.532a1.532,1.532,0,0,1,3.064,0V35.809A1.533,1.533,0,0,1,1.532,37.339Z"
+        fill="#f1ca23" />
+    </g>
+    <text id="studio" transform="translate(542 353.2)" fill="#fff" font-size="24"
+      font-family="NotoSans-ExtraLight, Noto Sans" font-weight="200">
+      <tspan x="0" y="0">Library</tspan>
+    </text>
+  </g>
+</svg>
+
+</div>
+</a>
+</div>
+</div>
+<div class="hidden h-full md:flex md:flex-grow justify-end">
+<ul class="flex h-16 mr-8">
+@foreach ($nav as $item)
+<li class="flex ml-4 text-sm relative inline-flex items-center pt-1 border-b-2 font-medium leading-5 transition duration-150 ease-in-out  border-transparent text-gray-400 hover:text-primary hover:border-primary focus:outline-none focus:text-primary focus:border-gray-300  ">
+<div class="flex w-full h-full">
+<a class="w-full flex items-center h-full px-3" href="{{ $item['link'] }}">{{ $item['title'] }}</a>
+</div>
+</li>
+@endforeach
+<li class="flex ml-4 text-sm relative inline-flex items-center pt-1 border-b-2 font-medium leading-5 transition duration-150 ease-in-out  border-transparent text-gray-400 hover:text-primary hover:border-primary focus:outline-none focus:text-primary focus:border-gray-300  ">
+<div class="flex w-full h-full">
+<a class="w-full flex items-center h-full px-3" href="#contact">联络我们</a>
+</div> </li>
+</ul>
+</div>
+</nav>
+</div>
+<div class="flex items-center md:hidden justify-end">
+<button @click="show_mobile_nav = true" aria-label="Mobile menu" type="button" class="text-gray-200 inline-flex items-center justify-center p-2 mr-2 rounded-md focus:outline-none transition duration-150 ease-in-out">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-menu inline-block current-color h-8 w-8" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<line x1="4" y1="8" x2="20" y2="8" />
+<line x1="4" y1="16" x2="20" y2="16" />
+</svg>
+</button>
+</div>
+</header>
+<section id="hero" class="relative  overflow-hidden ">
+<img class="background-image absolute inset-0 object-cover h-full w-full" alt="Hero Image" src="{{ URL::asset('assets/images/hero.jpg') }}" />
+<div class="absolute inset-0 bg-cover bg-center bg-no-repeat" style="background-image: linear-gradient(to bottom, rgba(34,34,34,0.9), rgba(34,34,34,0.4));"></div>
+<div class="xl:container xl:mx-auto md:px-6 px-4 relative pt-32 md:pt-40 lg:pt-48 xl:pt-56 pb-16 md:pb-20 lg:pb-24 xl:pb-32">
+<div class="flex text-center justify-center">
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2">
+<h1 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-100">
+{{ $title }}
+</h1>
+<div class="mt-3 text-gray-200 text-lg md:text-xl">
+<p>{{ $subtitle }}</p>
+</div>
+<div class="mt-5 mt-8 flex space-x-4 justify-center">
+<div class="rounded-md shadow" style="display:none;">
+<a href="https://learn.getgrav.org" class="bg-primary hover:bg-gray-800 text-white w-full flex items-center justify-center px-8 py-3 border border-transparent text-base leading-6 font-medium rounded-md focus:outline-none focus:ring transition duration-300 ease-in-out md:py-4 md:text-lg md:px-10">
+Read the documentation
+</a>
+</div>
+</div>
+</div>
+</div>
+</div>
+</section>
+<section class="flex-1">
+<div class="pt-0">
+<div id="highlights"></div>
+<div class="bg-white dark:bg-gray-900 py-8 md:py-24">
+<div class="xl:container xl:mx-auto md:px-6 px-4">
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
+<div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
+让学习圣典变得更容易
+</div>
+<h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
+我们的愿景
+</h2>
+<div class="mt-3 text-base md:text-lg lg:text-xl prose">
+<p>打造一个公共的巴利圣典学习平台。</p>
+</div>
+</div>
+<div class="grid md:grid-cols-2 xl:grid-cols-3 gap-y-8 gap-x-8 ">
+<div class="flex duration-300 hover:scale-105">
+<div class="flex-shrink-0">
+<div class="bg-primary text-gray-200 rounded-md p-3">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-gauge inline-block w-8 h-8 stroke-current" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<circle cx="12" cy="12" r="9" />
+<circle cx="12" cy="12" r="1" />
+<line x1="13.41" y1="10.59" x2="16" y2="8" />
+<path d="M7 12a5 5 0 0 1 5 -5" />
+</svg>
+</div>
+</div>
+
+<div class="ml-4">
+<a class="text-primary hover:text-primary-darker dark:hover:text-primary-lighter" href="https://getgrav.org"></a>
+<h3 class="font-bold mb-2 text-lg">
+翻译一套三藏
+</h3>
+
+<div class="prose">
+我们希望把完整的巴利三藏、义注、复注、nissaya都翻译成为中文。
+</div>
+</div>
+</div>
+<div class="flex duration-300 hover:scale-105">
+<div class="flex-shrink-0">
+<div class="bg-primary text-gray-200 rounded-md p-3">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-forklift inline-block w-8 h-8 stroke-current" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<circle cx="5" cy="17" r="2" />
+<circle cx="14" cy="17" r="2" />
+<line x1="7" y1="17" x2="12" y2="17" />
+<path d="M3 17v-6h13v6" />
+<path d="M5 11v-4h4" />
+<path d="M9 11v-6h4l3 6" />
+<path d="M22 15h-3v-10" />
+<line x1="16" y1="13" x2="19" y2="13" />
+</svg>
+</div>
+</div>
+
+<div class="ml-4">
+<a class="text-primary hover:text-primary-darker dark:hover:text-primary-lighter" href="https://learn.getgrav.org"></a>
+<h3 class="font-bold mb-2 text-lg">
+整理一本词典
+</h3>
+<div class="prose">
+我们希望借助沉淀下来的数据,整理一套完整的巴中字典。这项工作将在整个三藏翻译的过程中逐渐完成。
+</div>
+</div>
+</div>
+<div class="flex duration-300 hover:scale-105">
+<div class="flex-shrink-0">
+<div class="bg-primary text-gray-200 rounded-md p-3">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-box inline-block w-8 h-8 stroke-current" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<polyline points="12 3 20 7.5 20 16.5 12 21 4 16.5 4 7.5 12 3" />
+<line x1="12" y1="12" x2="20" y2="7.5" />
+<line x1="12" y1="12" x2="12" y2="21" />
+<line x1="12" y1="12" x2="4" y2="7.5" />
+</svg>
+</div>
+</div>
+<div class="ml-4">
+<h3 class="font-bold mb-2 text-lg">
+开发一个平台
+</h3>
+<div class="prose">
+我们会持续开发和维护wikipali平台,并不断发展新的功能,令其越来越方便与巴利翻译和研究。
+</div>
+</div>
+</div>
+
+</div>
+</div>
+</div>
+<div id="callout"></div>
+<div class="bg-white dark:bg-gray-900 py-8 md:py-24">
+<div class="xl:container xl:mx-auto md:px-6 px-4">
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
+<div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
+翻译中心
+</div>
+<h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
+wikipali(云台)翻译中心
+</h2>
+</div>
+<div class="flex md:flex-row-reverse justify-center md:justify-start flex-wrap md:flex-no-wrap">
+<div class="w-full md:w-1/2 md:pl-8 ">
+<img class="mb-8 md:mb-0 rounded-md" alt="/assets/gallery/03.jpg" src="/assets/gallery/02.jpg">
+</div>
+<div class="w-full md:w-1/2 md:pr-8 md:pr-8 mb-4 md:mb-0">
+<div class="md:text-lg prose">
+<p>wikipali(云台)翻译中心坐落于云南省昆明市宜良县,致力于整理三藏和佛教文献的翻译和研究。</p>
+<p>佛教文化是中国传统文化的一部分,也是全世界人类的宝贵文化遗产。然而, 由于很多佛教文献是由巴利语、梵语、缅语、泰文等记载,所以需要培养专门的小语种语言人才,进行相关的研究。</p>
+<p>我们的团队由一群热爱佛教文化的志愿者组成,来自不同的国家和地区,拥有不同的背景和经验。大家互相配合,致力于将佛教文献翻译成汉语,让更多人能够阅读和理解这些珍贵的文献</p>
+<p>我们的翻译工作主要集中在三藏经典,将传统的文献转换成为通俗的现代汉语。除此之外,我们也参与翻译佛教论著、传记、诗歌等多种形式的佛教文献,保留和研究其文学、历史价值。</p>
+<p>我们的翻译成果将会在wikipali网站上发布,供大家免费使用和阅读。感谢您对wikipali(云台)翻译中心的支持和关注!</p>
+<p style="display:none;"><a href="https://getgrav.org" class="btn mt-4 w-content block">Find out more...</a></p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div id="columns"></div>
+<div class="bg-gray-100 dark:bg-gray-800 py-8 md:py-24">
+<div class="xl:container xl:mx-auto md:px-6 px-4">
+<div class>
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
+<div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
+千年译经路
+</div>
+<h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
+巴利三藏翻译工程
+</h2>
+</div>
+<div class="mt-3 prose col-count-1 sm:col-count-2 sm:gap-x-6 lg:col-count-3 lg:gap-x-10 xl:col-count-4 xl:gap-x-12">
+<p>上座部佛教相信,佛陀讲经说法时所使用的语言是当时中印度马嘎塔国(Magadha,摩揭陀国)一带的民众方言——马嘎塔口语。这种语言在西元前3世纪的阿首咖王(Asoka,阿育王)时代即随着到斯里兰卡传播佛教的马兴德阿拉汉而传到斯里兰卡,并一直流传到今天。</p>
+<p>由于新哈勒人原先就有了自己的语言,当以马嘎塔语为媒介语的上座部佛教传播到斯里兰卡之后,这种语言就只是作为传诵三藏圣典之用,因此马嘎塔语又被称为「巴利语」。</p>
+<p>义注起源于佛陀在世时弟子们对佛陀教导的解释,如收录于《中部》的《法嗣经》《谛分别经》《应习不应习经》等,即是沙利补答尊者详细解释佛陀简短开示的经典。佛陀入灭后,诸圣者、大长老们继续对三藏圣典进行注解诠释,这些注释文献即是上座部佛教的「义注」,它们是上座部佛教历代长老大德们传承佛陀教法的禅修精要和智慧结晶,也是对巴利语三藏圣典最为权威的解释。</p>
+<p>之后的古代的大长老们又用巴利语撰写了解释义注和根本的再注释书——复注。这些古代文献已经有上千年的历史。</p>
+<p>根据上座部佛教传统,巴利语三藏包含了根本,义注,复注在内的100多本书。共计800万单词。长期以来,这些文献以巴利语刻写在棕榈叶上在东南亚国家流传。</p>
+<p>将这些古代文献翻译为汉语,是众多佛教徒的心愿。</p>
+<p><a href="/pcd/community/list" class="font-bold">已经翻译的经文</a></p>
+</div>
+</div>
+</div>
+</div>
+<div id="gallery"></div>
+<div class="bg-white dark:bg-gray-900 py-8 md:py-24">
+<div class="xl:container xl:mx-auto md:px-6 px-4">
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
+<div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
+欢迎加入翻译中心
+</div>
+<h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
+翻译中心相册
+</h2>
+<div class="mt-3 text-base md:text-lg lg:text-xl prose">
+<p></p>
+</div>
+</div>
+<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-y-2 gap-x-2">
+
+@foreach ($gallery as $item)
+<div class="inline-flex overflow-hidden rounded-md group safari-corner-overflow-fix">
+<a href="{{ $item['image'] }}" class="glightbox inline-block" data-gallery="161b91e52e3b1158306a38186e6f5107" data-title="{{ $item['title'] }}" data-description=".{{ $item['id'] }}" data-type="image">
+<img class="duration-200 group-hover:scale-110 group-hover:filter group-hover:brightness-110" title="Climbing Hilly Peaks" alt="Climbing Hilly Peaks" src="{{ $item['image'] }}" />
+</a>
+</div>
+@endforeach
+
+
+<div class="hidden">
+@foreach ($gallery as $item)
+    <div class="glightbox-desc {{ $item['id'] }}">
+        <div class="prose">
+            <p>{{ $item['description'] }}</p>
+        </div>
+    </div>
+@endforeach
+
+</div>
+</div>
+</div>
+</div>
+<div id="features"></div>
+<div class="bg-white dark:bg-gray-900 py-8 md:py-24">
+<div class="xl:container xl:mx-auto md:px-6 px-4">
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
+<div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
+在线翻译|协作|巴利语学习
+</div>
+<h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
+专门开发的巴利语学习和翻译工具
+</h2>
+<div class="mt-3 text-base md:text-lg lg:text-xl prose">
+<p>结合多年的教学与翻译经验。专门为巴利语教学和翻译定制的多种在线编辑工具。无需安装软件。支持各种桌面操作系统。</p>
+</div>
+</div>
+<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-y-8 gap-x-8 ">
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-pencil inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M4 20h4l10.5 -10.5a1.5 1.5 0 0 0 -4 -4l-10.5 10.5v4" />
+<line x1="13.5" y1="6.5" x2="17.5" y2="10.5" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+在线翻译
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-template inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<rect x="4" y="4" width="16" height="4" rx="1" />
+<rect x="4" y="12" width="6" height="8" rx="1" />
+<line x1="14" y1="12" x2="20" y2="12" />
+<line x1="14" y1="16" x2="20" y2="16" />
+<line x1="14" y1="20" x2="20" y2="20" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+术语模版
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-bolt inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<polyline points="13 3 13 10 19 10 11 21 11 14 5 14 13 3" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+实时发布译文
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-tag inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<circle cx="8.5" cy="8.5" r="1" fill="currentColor" />
+<path d="M4 7v3.859c0 .537 .213 1.052 .593 1.432l8.116 8.116a2.025 2.025 0 0 0 2.864 0l4.834 -4.834a2.025 2.025 0 0 0 0 -2.864l-8.117 -8.116a2.025 2.025 0 0 0 -1.431 -.593h-3.859a3 3 0 0 0 -3 3z" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+在线讨论校对
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-cloud-download inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M19 18a3.5 3.5 0 0 0 0 -7h-1a5 4.5 0 0 0 -11 -2a4.6 4.4 0 0 0 -2.1 8.4" />
+<line x1="12" y1="13" x2="12" y2="22" />
+<polyline points="9 19 12 22 15 19" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+译文导出
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105"  style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-apps inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<rect x="4" y="4" width="6" height="6" rx="1" />
+<rect x="4" y="14" width="6" height="6" rx="1" />
+<rect x="14" y="14" width="6" height="6" rx="1" />
+<line x1="14" y1="7" x2="20" y2="7" />
+<line x1="17" y1="4" x2="17" y2="10" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+Powerful Plugins
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105"  style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-device-desktop inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<rect x="3" y="4" width="18" height="12" rx="1" />
+<line x1="7" y1="20" x2="17" y2="20" />
+<line x1="9" y1="16" x2="9" y2="20" />
+<line x1="15" y1="16" x2="15" y2="20" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+Intuitive UI
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-text inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M14 3v4a1 1 0 0 0 1 1h4" />
+<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z" />
+<line x1="9" y1="9" x2="10" y2="9" />
+<line x1="9" y1="13" x2="15" y2="13" />
+<line x1="9" y1="17" x2="15" y2="17" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+社区字典
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-language inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M4 5h7" />
+<path d="M9 3v2c0 4.418 -2.239 8 -5 8" />
+<path d="M5 9c-.003 2.144 2.952 3.908 6.7 4" />
+<path d="M12 20l4 -9l4 9" />
+<path d="M19.1 18h-6.2" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+多语言译文对照
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105"  style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-users inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<circle cx="9" cy="7" r="4" />
+<path d="M3 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" />
+<path d="M16 3.13a4 4 0 0 1 0 7.75" />
+<path d="M21 21v-2a4 4 0 0 0 -3 -3.85" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+多人协作
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105"  style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-photo inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<line x1="15" y1="8" x2="15.01" y2="8" />
+<rect x="4" y="4" width="16" height="16" rx="3" />
+<path d="M4 15l4 -4a3 5 0 0 1 3 0l5 5" />
+<path d="M14 14l1 -1a3 5 0 0 1 3 0l2 2" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+Image Processing
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-notebook inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M6 4h11a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-11a1 1 0 0 1 -1 -1v-14a1 1 0 0 1 1 -1m3 0v18" />
+<line x1="13" y1="8" x2="15" y2="8" />
+<line x1="13" y1="12" x2="15" y2="12" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+文集
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105"  style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-github inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+On Github
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-device-mobile inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<rect x="7" y="4" width="10" height="16" rx="1" />
+<line x1="11" y1="5" x2="13" y2="5" />
+<line x1="12" y1="17" x2="12" y2="17.01" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+手机APP
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105"  style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-terminal inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M5 7l5 5l-5 5" />
+<line x1="12" y1="19" x2="19" y2="19" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+CLI Power
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+
+<div class="flex duration-300 hover:scale-105" style="display:none;">
+<div class="flex flex-col w-full rounded-md group hover:bg-gray-100 dark:hover:bg-gray-800">
+<div class="text-gray-500 group-hover:text-primary rounded-md p-3 text-center">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-gift inline-block w-16 h-16 stroke-current stroke-3/2 mx-auto" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<rect x="3" y="8" width="18" height="4" rx="1" />
+<line x1="12" y1="8" x2="12" y2="21" />
+<path d="M19 12v7a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-7" />
+<path d="M7.5 8a2.5 2.5 0 0 1 0 -5a4.8 8 0 0 1 4.5 5a4.8 8 0 0 1 4.5 -5a2.5 2.5 0 0 1 0 5" />
+</svg>
+</div>
+<h3 class="mb-2 text-base md:text-lg group-hover:text-primary text-center">
+Awesomazing
+</h3>
+<div class="prose text-center">
+</div>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div id="contact"></div>
+<div class="bg-primary-darker text-primary-lighter py-8 md:py-24">
+<div class="xl:container xl:mx-auto md:px-6 px-4">
+<div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
+<div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-300">
+更多关于我们的信息
+</div>
+<h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-100">
+联络我们
+</h2>
+<div class="mt-3 text-base md:text-lg lg:text-xl prose">
+<p>wikipali@126.com</p>
+<p><span>请点击<a href="https://wj.qq.com/s2/13364158/ba70/" target="_blank" rel="noreferrer">腾讯问卷</a>填写您的反馈</span></p>
+</div>
+</div>
+<div class="max-w-xl mx-auto text-gray-700" style="display:none;">
+<form name="contact" action="#contact-us" method="POST" id="contact" class=" ">
+<div class="form-field  ">
+<div class="form-data" data-grav-field="text" data-grav-disabled data-grav-default="null">
+<div class="form-input-wrapper ">
+<input name="data[name]" value type="text" class="form-input " placeholder="Your&#x20;full&#x20;name" autocomplete="on" required="required" />
+</div>
+</div>
+</div>
+<div class="form-field  ">
+<div class="form-data" data-grav-field="email" data-grav-disabled data-grav-default="null">
+<div class="form-input-wrapper ">
+<input name="data[email]" value type="email" class="form-input " placeholder="Your&#x20;email&#x20;address" required="required" />
+</div>
+</div>
+</div>
+<div class="form-field  ">
+<div class="form-data" data-grav-field="text" data-grav-disabled data-grav-default="null">
+<div class="form-input-wrapper ">
+<input name="data[phone]" value type="text" class="form-input " placeholder="Your&#x20;phone&#x20;number&#x20;&#x28;optional&#x29;" />
+</div>
+</div>
+</div>
+<div class="form-field  ">
+<div class="form-data" data-grav-field="textarea" data-grav-disabled data-grav-default="null">
+<div class="form-textarea-wrapper  ">
+<textarea name="data[message]" class="form-textarea  " placeholder="Your message" required="required" rows="4"></textarea>
+</div>
+</div>
+</div>
+<input type="hidden" name="__form-name__" value="contact" />
+<input type="hidden" name="__unique_form_id__" value="ix2wof4ukdnschp3i1bc" />
+<input type="hidden" name="form-nonce" value="a4618725adb330b36829801d1b7a11d2" />
+<div class="text-center">
+<button type="submit" class="form-button no-default-style text-white bg-gray-700 hover:bg-primary">Submit Form</button>
+</div>
+</form>
+</div>
+</div>
+</div>
+</div>
+</section>
+<footer class="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 text-sm">
+<div class="xl:container xl:mx-auto md:px-6 px-4 py-8">
+<div class="relative flex flex-col md:flex-row justify-between min-h-16 text-gray-600 dark:text-gray-500">
+    <div style="text-align: center;">
+        <span>ICP:<a href="https://beian.miit.gov.cn/" target="_blank" rel="noreferrer">滇ICP备2023006988号-1</a></span>
+    </div>
+</div>
+<div style="display:none;" class="relative flex flex-col md:flex-row justify-between min-h-16 text-gray-600 dark:text-gray-500">
+    <div class="flex font-medium space-x-6 md:space-x-8 items-center justify-center mb-6 md:mb-0 md:justify-start ">
+    <a href="/typhoon/onepage/#" class="hover:text-primary transition duration-300">Terms &amp; Conditions</a>
+    <a href="/typhoon/onepage/#" class="hover:text-primary transition duration-300">Privacy Policy</a>
+    </div>
+    <div class="flex social mb-2 space-x-2 items-center justify-center mb-6 md:mb-0 md:justify-start">
+    <a href="https://twitter.com/getgrav" aria-label="twitter" class="text-twitter  hover:filter hover:brightness-110 transition duration-300">
+    <svg class="inline-block w-8 h-8 fill-current stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 960"><path d="M480 0c133.333 0 246.667 46.667 340 140s140 206.667 140 340c0 132-46.667 245-140 339S613.333 960 480 960c-132 0-245-47-339-141S0 612 0 480c0-133.333 47-246.667 141-340S348 0 480 0m196 392c20-14.667 35.333-30.667 46-48-21.333 8-39.333 12.667-54 14 20-12 34-29.333 42-52-20 10.667-40 18-60 22-18.667-18.667-42-28-70-28-26.667 0-49 9.333-67 28s-27 40.667-27 66c0 1.333.333 4.667 1 10s1 9.333 1 12c-80-4-144.667-37.333-194-100-9.333 16-14 32-14 48 0 33.333 14.667 59.333 44 78-17.333 0-32-4-44-12v2c0 22.667 7 42.667 21 60s32.333 28 55 32c-10.667 2.667-18.667 4-24 4-8 0-14-.667-18-2 13.333 44 42.667 66 88 66-33.333 26.667-72.667 40-118 40h-22c45.333 28 93.333 42 144 42 81.333 0 146.667-27.667 196-83s74-117.667 74-187v-12" /></svg>
+    </a>
+    <a href="https://github.com/getgrav" aria-label="github" class="text-github dark:text-gray-500 hover:filter hover:brightness-110 transition duration-300">
+    <svg class="inline-block w-8 h-8 fill-current stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 960"><path d="M480 476c10.667 0 24.667-.667 42-2s31-2 41-2 20.667 1.333 32 4c11.333 2.667 21 7.333 29 14 17.333 17.333 26 37.333 26 60 0 42.667-14.333 70.667-43 84-28.667 13.333-71 20-127 20s-98.333-6.667-127-20-43-41.333-43-84c0-22.667 8.667-42.667 26-60 8-6.667 17.667-11.333 29-14 11.333-2.667 22-4 32-4s23.667.667 41 2 31.333 2 42 2m-80 128c9.333 0 17-4.667 23-14s9-20 9-32c0-30.667-10.667-46-32-46s-32 15.333-32 46c0 12 3 22.667 9 32 6 9.333 13.667 14 23 14m160 0c9.333 0 17.333-4.667 24-14 6.667-9.333 10-20 10-32 0-13.333-3.333-24.333-10-33-6.667-8.667-14.667-13-24-13-21.333 0-32 15.333-32 46 0 12 3 22.667 9 32 6 9.333 13.667 14 23 14M480 0c133.333 0 246.667 46.667 340 140s140 206.667 140 340c0 132-46.667 245-140 339S613.333 960 480 960c-132 0-245-47-339-141S0 612 0 480c0-133.333 47-246.667 141-340S348 0 480 0m44 676c125.333 0 188-61.333 188-184 0-37.333-12.667-70-38-98 2.667-2.667 3-16.333 1-41s-7.667-48.333-17-71c-29.333 4-67.333 21.333-114 52-13.333-4-34.667-6-64-6-26.667 0-48 2-64 6-20-13.333-39.667-24.333-59-33-19.333-8.667-33-14.333-41-17l-14-2c-9.333 22.667-15 46.333-17 71s-1.667 38.333 1 41c-25.333 28-38 60.667-38 98 0 122.667 62.667 184 188 184h88" /></svg>
+    </a>
+    </div>
+</div>
+<div class="relative flex flex-col md:flex-row justify-between mt-0 md:mt-6 text-gray-600 dark:text-gray-500 md:items-center">
+<div class="text-center md:text-left mb-6 md:mb-0">
+<p><a href="https://getgrav.org" class="hover:text-primary font-bold">Grav</a> was
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-code inline-block text-gray-700 dark:text-gray-400 w-4 h-4" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<polyline points="7 8 3 12 7 16" />
+<polyline points="17 8 21 12 17 16" />
+<line x1="14" y1="4" x2="10" y2="20" />
+</svg> with
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-heart inline-block text-red-700 w-4 h-4 animate-pulse" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" />
+</svg> by
+<a href="https://trilby.media" class="hover:text-primary font-bold">Trilby Media, LLC</a></p>
+</div>
+<div class="max-w-64 mx-auto md:mx-0">
+<div class="theme-chooser flex items-center rounded-md border border-gray-300 dark:border-gray-600 pl-2 pr-0">
+<span>
+<span x-cloak :class="{'inline-block' : appearance === 'system', 'hidden': appearance !== 'system'}">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-device-desktop inline-block w-6 h-6 stroke-current" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<rect x="3" y="4" width="18" height="12" rx="1" />
+<line x1="7" y1="20" x2="17" y2="20" />
+<line x1="9" y1="16" x2="9" y2="20" />
+<line x1="15" y1="16" x2="15" y2="20" />
+</svg>
+</span>
+<span x-cloak :class="{'inline-block' : appearance === 'light', 'hidden': appearance !== 'light'}">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-sun inline-block w-6 h-6 stroke-current" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<circle cx="12" cy="12" r="4" />
+<path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" />
+</svg>
+</span>
+<span x-cloak :class="{'inline-block' : appearance === 'dark', 'hidden': appearance !== 'dark'}">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-moon inline-block w-6 h-6 stroke-current" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
+</svg>
+</span>
+</span>
+<select x-model="appearance" @change="typhoonStore({ appearance: $event.target.value });" class="form-select focus:ring-transparent focus:outline-none focus:shadow-none" aria-label="Change color theme">
+<option value="system" :selected="appearance === 'system'">System</option>
+<option value="light" :selected="appearance === 'light'">Light</option>
+<option value="dark" :selected="appearance === 'dark'">Dark</option>
+</select>
+</div>
+</div>
+</div>
+</div>
+</footer>
+<div :class="{ 'invisible': !show_mobile_nav, 'opacity-100': show_mobile_nav }" class="mobile-nav invisible z-20 overflow-hidden transition duration-500 h-screen w-screen absolute top-0 flex flex-col items-center justify-around absolute opacity-0 bg-gray-800 transition duration-500">
+<div class="overflow-y-auto w-full py-12 pl-2 pr-12 sm:p-12">
+<ul class="flex flex-col text-gray-300 text-left w-full">
+<li x-data="{ selected: true }" class="text-lg pl-2 border-t border-gray-700 py-2 active ">
+<div class="flex w-full h-full">
+<a class="w-full transition duration-300 hover:text-primary" href="/typhoon/onepage/">Modular</a>
+</div> </li>
+<li x-data="{ selected: false }" class="text-lg pl-2 border-t border-gray-700 py-2  ">
+<div class="flex w-full h-full">
+<a class="w-full transition duration-300 hover:text-primary" href="/typhoon/onepage/typography">Typography</a>
+</div> </li>
+<li x-data="{ selected: false }" class="text-lg pl-2 border-t border-gray-700 py-2  ">
+<div class="flex w-full h-full">
+<a class="w-full transition duration-300 hover:text-primary" href="/typhoon/onepage/start-here">Start Here</a>
+</div> </li>
+</ul>
+</div>
+<div class="absolute top-2 right-2">
+<button @click="show_mobile_nav = false" aria-label="Close button" type="button" class="inline-flex items-center justify-center p-2 rounded-md text-white hover:text-primary focus:outline-none focus:text-gray-800 transition duration-150 ease-in-out">
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x inline-block h-8 w-8 text-gray-300 hover:text-white" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+<path stroke="none" d="M0 0h24v24H0z" fill="none" />
+<line x1="18" y1="6" x2="6" y2="18" />
+<line x1="6" y1="6" x2="18" y2="18" />
+</svg>
+</button>
+</div>
+</div>
+</div>
+<script>
+window.GravForm = window.GravForm || {};
+    window.GravForm.config = {
+        current_url: '/typhoon/onepage',
+        current_params: [],
+        param_sep: ':',
+        base_url_relative: '/typhoon/onepage',
+        form_nonce: 'a4618725adb330b36829801d1b7a11d2',
+        session_timeout: 1800
+    };
+    window.GravForm.translations = Object.assign({}, window.GravForm.translations || {}, { PLUGIN_FORM: {} });
+</script>
+<script src="/assets/typhoon/js/glightbox.min.js"></script>
+<script src="/assets/typhoon/js/appearance.js"></script>
+<script>
+const lightbox = GLightbox({"selector":"[rel=\"lightbox\"], .glightbox","width":"90vw","height":"auto"});
+</script>
+</body>
+</html>

+ 2 - 0
routes/api.php

@@ -75,6 +75,7 @@ use App\Http\Controllers\DictInfoController;
 use App\Http\Controllers\PgPaliDictDownloadController;
 use App\Http\Controllers\SearchPaliDataController;
 use App\Http\Controllers\SearchPaliWbwController;
+use App\Http\Controllers\SearchPageNumberController;
 
 /*
 |--------------------------------------------------------------------------
@@ -203,6 +204,7 @@ Route::group(['prefix' => 'v2'],function(){
     Route::apiResource('pali-search-data',SearchPaliDataController::class);
     Route::apiResource('search-pali-wbw',SearchPaliWbwController::class);
     Route::get('search-pali-wbw-books',[SearchPaliWbwController::class,'book_list']);
+    Route::apiResource('search-page-number',SearchPageNumberController::class);
 
     Route::get('download/{type1}/{type2}/{uuid}/{filename}', function ($type1,$type2,$uuid,$filename) {
         header("Content-Type: {$type1}/{$type1}");

+ 1 - 1
routes/web.php

@@ -20,7 +20,7 @@ Route::redirect('/', '/pcd/community/list');
 Route::redirect('/app', '/app/pcdl/index.php');
 Route::redirect('/app/pcdl', '/app/pcdl/index.php');
 
-//Route::get('/', [PageIndexController::class,'index']);
+Route::get('/', [PageIndexController::class,'index']);
 
 Route::get('/api/sentence/progress/image', [SentenceInfoController::class,'showprogress']);
 Route::get('/api/sentence/progress/daily/image', [SentenceInfoController::class,'showprogressdaily']);

Некоторые файлы не были показаны из-за большого количества измененных файлов