feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run

- Base: wintercms/winter branch 1.2 (full framework)
- Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS
- Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication
- Partials: hero (offline-first), features, modes (offline/nube toggle),
  screenshots, pricing (3 planes), comparison, FAQ, CTA
- Plugin VivesPOS.Site with ContactForm
- Dockerfile: PHP 8.2 Apache, port 80, healthcheck
- Added winter/wn-pages, blog, sitemap, seo plugins
- Active theme set to vivespos
This commit is contained in:
2026-08-21 19:29:00 -06:00
commit 1f72193a64
3266 changed files with 531480 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
del {
text-decoration: none;
color: #b30000;
background: #fadad7;
}
ins {
background: #eaf2c2;
color: #406619;
text-decoration: none;
}

View File

@@ -0,0 +1,104 @@
.draggable-component-item,
.component-list .components div.layout-cell,
div.control-componentlist div.components div.layout-cell{font-size:11px;cursor:pointer;background:#fff;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.draggable-component-item:hover,
.component-list .components div.layout-cell:hover,
div.control-componentlist div.components div.layout-cell:hover{background:#6cc551}
.draggable-component-item>div,
.component-list .components div.layout-cell>div,
div.control-componentlist div.components div.layout-cell>div{white-space:normal;color:#475354;position:relative;border-right:1px solid #ECF0F1}
.draggable-component-item>div:before,
.component-list .components div.layout-cell>div:before,
div.control-componentlist div.components div.layout-cell>div:before{position:absolute;font-size:16px;left:15px;top:7px;opacity:0.7;filter:alpha(opacity=70)}
.draggable-component-item>div:hover,
.component-list .components div.layout-cell>div:hover,
div.control-componentlist div.components div.layout-cell>div:hover{color:#fff}
.draggable-component-item>div:hover:before,
.component-list .components div.layout-cell>div:hover:before,
div.control-componentlist div.components div.layout-cell>div:hover:before{opacity:1;filter:alpha(opacity=100)}
.draggable-component-item>div:after,
.component-list .components div.layout-cell>div:after,
div.control-componentlist div.components div.layout-cell>div:after{position:absolute;font-size:37px;top:1px;z-index:50;color:#fff;text-shadow:0 0 1px #475354;width:12px;overflow:hidden;text-indent:-25px;right:-12px;font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f12e"}
.draggable-component-item>div:hover:after,
.component-list .components div.layout-cell>div:hover:after,
div.control-componentlist div.components div.layout-cell>div:hover:after{text-shadow:none;color:#6cc551}
.draggable-component-item>div span,
.component-list .components div.layout-cell>div span,
div.control-componentlist div.components div.layout-cell>div span{display:block}
.draggable-component-item>div span.name,
.component-list .components div.layout-cell>div span.name,
div.control-componentlist div.components div.layout-cell>div span.name{white-space:nowrap;padding:8px 15px 0;font-weight:400;line-height:20px;font-size:13px}
.draggable-component-item>div span.description,
.component-list .components div.layout-cell>div span.description,
div.control-componentlist div.components div.layout-cell>div span.description{padding:0 15px 10px;margin-top:8px;font-weight:400;font-size:11px;line-height:150%}
.draggable-component-item.placeholder>div,
.component-list .components div.layout-cell.placeholder>div,
div.control-componentlist div.components div.layout-cell.placeholder>div{background:#e0e0e0;color:#e0e0e0}
.draggable-component-item.placeholder>div:before,
.component-list .components div.layout-cell.placeholder>div:before,
div.control-componentlist div.components div.layout-cell.placeholder>div:before,
.draggable-component-item.placeholder>div:after,
.component-list .components div.layout-cell.placeholder>div:after,
div.control-componentlist div.components div.layout-cell.placeholder>div:after{color:#e0e0e0 !important;text-shadow:none !important}
[data-field-name="components"] + .control-tabs.primary-tabs{margin-top:-10px}
div.control-componentlist{position:relative;padding:0;-webkit-transition:all 0.3s ease;transition:all 0.3s ease}
div.control-componentlist.droppable{background-color:#79cbe1}
div.control-componentlist.has-components{padding:0 20px 20px}
div.control-componentlist div.layout{width:auto}
div.control-componentlist div.components div.layout-cell.error-component{background:#e01346}
div.control-componentlist div.components div.layout-cell.error-component>div{color:#fff}
div.control-componentlist div.components div.layout-cell.error-component>div:after{color:#e01346}
div.control-componentlist div.components div.layout-cell.warning-component{background:#ffc107}
div.control-componentlist div.components div.layout-cell.warning-component>div{color:#343a40}
div.control-componentlist div.components div.layout-cell.warning-component>div:after{color:#ffc107}
div.control-componentlist div.components div.layout-cell:first-child{border-bottom-left-radius:3px;border-top-left-radius:3px}
div.control-componentlist div.components div.layout-cell:last-child{margin-right:0;border-bottom-right-radius:3px;border-top-right-radius:3px}
div.control-componentlist div.components div.layout-cell:last-child>div{border-right:none}
div.control-componentlist div.components div.layout-cell:last-child>div:after{display:none}
div.control-componentlist div.components div.layout-cell:nth-child(2n)>div:after{top:auto;bottom:5px}
div.control-componentlist div.components div.layout-cell:first-child>div.popover-highlight{border-bottom-left-radius:3px;border-top-left-radius:3px}
div.control-componentlist div.components div.layout-cell:last-child>div.popover-highlight{border-bottom-right-radius:3px;border-top-right-radius:3px}
div.control-componentlist div.components div.layout-cell>div{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0);-webkit-transition:-webkit-transform 0.2s;-moz-transition:-moz-transform 0.2s;-o-transition:-o-transform 0.2s;transition:transform 0.2s;max-width:250px;min-width:170px}
div.control-componentlist div.components div.layout-cell>div.popover-highlight{border-right-color:rgba(0,0,0,0);background:#fff !important;color:#475354 !important}
div.control-componentlist div.components div.layout-cell>div.popover-highlight:before{opacity:0.7;filter:alpha(opacity=70)}
div.control-componentlist div.components div.layout-cell>div.popover-highlight:after{color:#fff;text-shadow:none}
div.control-componentlist div.components div.layout-cell>div.popover-highlight a.remove{display:none}
div.control-componentlist div.components div.layout-cell>div span.name{padding-left:38px}
div.control-componentlist div.components div.layout-cell>div span.description{padding-bottom:35px}
div.control-componentlist div.components div.layout-cell>div span.alias{padding:0 15px;font-weight:500;position:absolute;width:100%;bottom:10px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
div.control-componentlist div.components div.layout-cell>div span.alias:before{margin-right:4px;opacity:0.55;filter:alpha(opacity=55)}
div.control-componentlist div.components div.layout-cell>div a.remove{position:absolute;display:inline-block;top:1px;right:5px;color:#000;font-size:17px;font-weight:bold;opacity:0.3;filter:alpha(opacity=30)}
div.control-componentlist div.components div.layout-cell>div a.remove:hover{opacity:0.5;filter:alpha(opacity=50);text-decoration:none}
div.control-componentlist div.components div.layout-cell.adding>div{-webkit-transform:translate(0,-100px) !important;-ms-transform:translate(0,-100px) !important;transform:translate(0,-100px) !important}
.draggable-component-item{opacity:0.6;filter:alpha(opacity=60)}
.draggable-component-item span.alias{display:none}
.draggable-component-item a.remove{display:none}
.component-list .components div.layout div.layout-row div.layout-cell{border-top:1px solid #ECF0F1}
.component-list .components div.layout div.layout-row div.layout-cell span.alias{display:none}
.component-list .components div.layout div.layout-row div.layout-cell a.remove{display:none}
.component-list .components div.layout div.layout-row div.layout-cell:last-child>div{border-right:none}
.component-list .components div.layout div.layout-row div.layout-cell:last-child>div:after{display:none}
.component-list .components div.layout div.layout-row div.layout-cell>div:before{position:absolute;font-size:37px;top:1px;z-index:50;color:#fff;text-shadow:0 0 1px #475354;width:12px;overflow:hidden;text-indent:-25px;right:-12px;font-family:"Font Awesome 6 Free";font-weight:900;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-style:normal;font-variant:normal;text-rendering:auto;content:"\f12e";-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg);opacity:1;filter:alpha(opacity=100);left:auto;top:-17px;right:15px}
.component-list .components div.layout div.layout-row div.layout-cell>div:hover:before{text-shadow:none;color:#6cc551}
.component-list .components div.layout div.layout-row:first-child div.layout-cell>div:before{display:none}
.component-list .components div.layout.single div.layout-row>div.layout-cell>div{border-right:none}
.component-list .components div.layout.single div.layout-row>div.layout-cell>div:before{display:block;right:55.5%}
.control-filelist.component-list ul li div.group{background:#f1f3f4;border-top:1px solid #e6ebed;padding:10px 15px 10px 10px;position:relative;cursor:pointer}
.control-filelist.component-list ul li div.group h4{text-transform:uppercase;font-size:14px;margin-top:3px}
.control-filelist.component-list ul li div.group h4 a{padding-left:33px}
.control-filelist.component-list ul li div.group h4 a:before{left:0}
.control-filelist.component-list ul li div.group h4 a:after{display:none}
.control-filelist.component-list ul li div.group span.description{display:block;font-size:13px;padding-left:33px;color:#8f8f8f}
.control-filelist.component-list ul li div.group i{position:absolute;left:22px;top:21px;font-size:16px;opacity:0.7;filter:alpha(opacity=70);color:#405261}
.touch div.control-componentlist div.components div.layout-cell>div:hover,
.touch div.control-componentlist div.components div.layout-cell>div:active,
.touch div.control-componentlist div.components div.layout-cell>div:active:focus{background:#fff;color:#475354}
.touch div.control-componentlist div.components div.layout-cell>div:hover:after,
.touch div.control-componentlist div.components div.layout-cell>div:active:after,
.touch div.control-componentlist div.components div.layout-cell>div:active:focus:after{text-shadow:0 0 1px #475354 !important;color:#fff !important}
body.drag div.control-componentlist div.components div.layout-cell>div,
body.drag div.control-componentlist div.components div.layout-cell>div:hover,
body.drag div.control-componentlist div.components div.layout-cell>div:active{background:#fff;color:#475354}
body.drag div.control-componentlist div.components div.layout-cell>div:after,
body.drag div.control-componentlist div.components div.layout-cell>div:hover:after,
body.drag div.control-componentlist div.components div.layout-cell>div:active:after{text-shadow:0 0 1px #475354 !important;color:#fff !important}

View File

@@ -0,0 +1,33 @@
.theme-selector-layout .layout-cell{padding:24px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
.theme-selector-layout .theme-thumbnail{width:288px;background:#ecf0f1;border-top:1px solid #e3e7e9}
.theme-selector-layout .theme-thumbnail img{opacity:0.6;filter:alpha(opacity=60);width:240px}
.theme-selector-layout .theme-description{border-top:1px solid #f2f3f4}
.theme-selector-layout .theme-description h3,
.theme-selector-layout .theme-description p{opacity:0.6;filter:alpha(opacity=60)}
.theme-selector-layout .theme-description h3{margin:0 0 25px 0;font-size:28px;color:#2b3e50;display:inline-block}
.theme-selector-layout .theme-description p.author{font-size:13px;display:inline-block;color:#808c8d}
.theme-selector-layout .theme-description p.description{color:#2b3e50;font-size:14px;line-height:180%;margin-bottom:30px}
.theme-selector-layout .theme-description .controls .btn>i{margin-right:5px;font-size:16px;position:relative;top:1px}
.theme-selector-layout .theme-description .controls .btn>i.icon-star{color:#f1a84e}
.theme-selector-layout .theme-description .controls .dropdown{display:inline-block}
.theme-selector-layout .layout-row.active .theme-thumbnail{background:#bdc3c7;border-top-color:#bdc3c7}
.theme-selector-layout .layout-row.active .thumbnail-container{position:relative}
.theme-selector-layout .layout-row.active .thumbnail-container:after{content:'';display:block;width:0;height:0;border-top:14px solid transparent;border-bottom:14px solid transparent;border-left:15px solid #bdc3c7;position:absolute;right:-35px;top:50%;margin-top:-14px}
.theme-selector-layout .layout-row.active .theme-description h3,
.theme-selector-layout .layout-row:hover .theme-description h3,
.theme-selector-layout .layout-row.active .theme-description p,
.theme-selector-layout .layout-row:hover .theme-description p{opacity:1;filter:alpha(opacity=100)}
.theme-selector-layout .layout-row.active .theme-thumbnail img,
.theme-selector-layout .layout-row:hover .theme-thumbnail img{opacity:1;filter:alpha(opacity=100)}
.theme-selector-layout .layout-row:first-child .theme-description,
.theme-selector-layout .layout-row.links .theme-description,
.theme-selector-layout .layout-row:first-child .theme-thumbnail,
.theme-selector-layout .layout-row.links .theme-thumbnail{border-top:none}
.theme-selector-layout .layout-row.links .theme-thumbnail{border-bottom:1px solid #e3e7e9}
.theme-selector-layout .layout-row.links .theme-description{border-bottom:1px solid #f2f3f4}
.theme-selector-layout .create-new-theme{margin-bottom:10px}
.theme-selector-layout .create-new-theme,
.theme-selector-layout .find-more-themes{background:#ecf0f1;color:#2b3e50;text-decoration:none;display:block;padding:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
.theme-selector-layout .create-new-theme:hover,
.theme-selector-layout .find-more-themes:hover{background:#2da7c7;color:white}
@media (max-width:768px){.theme-selector-layout .layout-cell,.theme-selector-layout .layout-row{display:block!important;width:auto!important;height:auto!important}.theme-selector-layout .theme-thumbnail img{width:100%}.theme-selector-layout .layout-row.links .theme-thumbnail{background:transparent;padding:0}}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="65px" height="64px" viewBox="0 0 65 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
<title>cms-icon</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="Group" sketch:type="MSLayerGroup">
<path d="M0.754,3.84 C0.754,1.719 2.473,0 4.594,0 L32.754,0 L48.754,13.44 L48.754,60.16 C48.754,62.281 47.035,64 44.914,64 L4.594,64 C2.473,64 0.754,62.281 0.754,60.16 L0.754,3.84 Z" id="Fill-637" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
<path d="M32.754,0 L32.754,9.6 C32.754,11.721 34.473,13.44 36.594,13.44 L48.754,13.44 L32.754,0 Z" id="Fill-638" fill="#F0F1F1" sketch:type="MSShapeGroup"></path>
<path d="M29.754,47 L39.754,47 C40.305,47 40.754,46.552 40.754,46 L40.754,41 C40.754,40.448 40.305,40 39.754,40 L29.754,40 C29.203,40 28.754,40.448 28.754,41 L28.754,46 C28.754,46.552 29.203,47 29.754,47" id="Fill-639" fill="#E2E4E5" sketch:type="MSShapeGroup"></path>
<path d="M29.754,40 C29.203,40 28.754,40.448 28.754,41 L28.754,46 C28.754,46.552 29.203,47 29.754,47 L39.754,47 C40.305,47 40.754,46.552 40.754,46 L40.754,41 C40.754,40.448 40.305,40 39.754,40 L29.754,40 Z M39.754,49 L29.754,49 C28.1,49 26.754,47.654 26.754,46 L26.754,41 C26.754,39.346 28.1,38 29.754,38 L39.754,38 C41.408,38 42.754,39.346 42.754,41 L42.754,46 C42.754,47.654 41.408,49 39.754,49 L39.754,49 Z" id="Fill-640" fill="#B6BCBD" sketch:type="MSShapeGroup"></path>
<path d="M7.754,47 L17.754,47 C18.305,47 18.754,46.552 18.754,46 L18.754,41 C18.754,40.448 18.305,40 17.754,40 L7.754,40 C7.203,40 6.754,40.448 6.754,41 L6.754,46 C6.754,46.552 7.203,47 7.754,47" id="Fill-641" fill="#E2E4E5" sketch:type="MSShapeGroup"></path>
<path d="M7.754,40 C7.203,40 6.754,40.448 6.754,41 L6.754,46 C6.754,46.552 7.203,47 7.754,47 L17.754,47 C18.305,47 18.754,46.552 18.754,46 L18.754,41 C18.754,40.448 18.305,40 17.754,40 L7.754,40 Z M17.754,49 L7.754,49 C6.1,49 4.754,47.654 4.754,46 L4.754,41 C4.754,39.346 6.1,38 7.754,38 L17.754,38 C19.408,38 20.754,39.346 20.754,41 L20.754,46 C20.754,47.654 19.408,49 17.754,49 L17.754,49 Z" id="Fill-642" fill="#B6BCBD" sketch:type="MSShapeGroup"></path>
<path d="M18.754,27 L28.754,27 C29.305,27 29.754,26.552 29.754,26 L29.754,21 C29.754,20.448 29.305,20 28.754,20 L18.754,20 C18.203,20 17.754,20.448 17.754,21 L17.754,26 C17.754,26.552 18.203,27 18.754,27" id="Fill-643" fill="#9CE5F4" sketch:type="MSShapeGroup"></path>
<path d="M28.026,29 L25.768,29 L30.482,38 L32.74,38 L28.026,29 Z" id="Fill-644" fill="#CFD3D4" sketch:type="MSShapeGroup"></path>
<path d="M19.482,29 L14.768,38 L17.026,38 L21.74,29 L19.482,29 Z" id="Fill-645" fill="#CFD3D4" sketch:type="MSShapeGroup"></path>
<path d="M18.754,20 C18.203,20 17.754,20.448 17.754,21 L17.754,26 C17.754,26.552 18.203,27 18.754,27 L28.754,27 C29.305,27 29.754,26.552 29.754,26 L29.754,21 C29.754,20.448 29.305,20 28.754,20 L18.754,20 Z M28.754,29 L18.754,29 C17.1,29 15.754,27.654 15.754,26 L15.754,21 C15.754,19.346 17.1,18 18.754,18 L28.754,18 C30.408,18 31.754,19.346 31.754,21 L31.754,26 C31.754,27.654 30.408,29 28.754,29 L28.754,29 Z" id="Fill-646" fill="#40C9E7" sketch:type="MSShapeGroup"></path>
<path d="M61.8979,34.4983 L40.6949,58.4653 L34.8179,59.6753 C34.6479,58.8573 34.2249,58.0823 33.5509,57.4863 C32.8769,56.8893 32.0559,56.5643 31.2229,56.4953 L31.7069,50.5143 L52.9099,26.5473 L61.8979,34.4983 Z" id="Fill-647" fill="#F4D0A1" sketch:type="MSShapeGroup"></path>
<path d="M61.8692,34.4729 C61.8852,34.4869 61.8862,34.5109 61.8722,34.5269 L41.1512,57.9499 C41.1182,57.9879 41.0652,57.9479 41.0892,57.9029 C41.7392,56.7029 41.5032,55.1759 40.4362,54.2309 C39.3092,53.2349 37.6512,53.2359 36.5312,54.1689 C36.4992,54.1959 36.4562,54.1589 36.4792,54.1229 C37.2692,52.8979 37.0682,51.2519 35.9422,50.2549 C34.8622,49.3009 33.2942,49.2619 32.1802,50.0819 C32.1442,50.1089 32.1032,50.0669 32.1332,50.0339 L52.8852,26.5759 C52.8992,26.5599 52.9232,26.5589 52.9392,26.5729 L61.8692,34.4729 Z" id="Fill-648" fill="#059BBF" sketch:type="MSShapeGroup"></path>
<path d="M61.8979,34.4983 L52.9099,26.5473 L54.2349,25.0493 L63.2229,33.0003 L61.8979,34.4983 Z" id="Fill-649" fill="#FACB1B" sketch:type="MSShapeGroup"></path>
<path d="M62.7497,24.5706 L62.6597,24.4916 C60.2027,22.3176 56.4487,22.5476 54.2747,25.0046 L54.2357,25.0496 L63.2227,33.0006 L63.2627,32.9556 C65.4367,30.4986 65.2067,26.7446 62.7497,24.5706" id="Fill-650" fill="#F89392" sketch:type="MSShapeGroup"></path>
<path d="M61.8728,34.5266 L41.1258,57.9796 C41.0948,58.0136 41.0478,57.9776 41.0698,57.9376 C41.7438,56.7326 41.5138,55.1846 40.4358,54.2306 C39.3118,53.2366 37.6588,53.2356 36.5388,54.1626 C36.5198,54.1786 36.4978,54.1546 36.5138,54.1366 L57.3788,30.5516 C57.3928,30.5356 57.4168,30.5336 57.4328,30.5476 L61.8698,34.4736 C61.8848,34.4866 61.8868,34.5106 61.8728,34.5266" id="Fill-651" fill="#0484AB" sketch:type="MSShapeGroup"></path>
<path d="M34.8181,59.6754 L30.9001,60.4824 L31.2231,56.4954 C32.0561,56.5644 32.8771,56.8894 33.5511,57.4864 C34.2251,58.0824 34.6481,58.8574 34.8181,59.6754" id="Fill-652" fill="#3E3E3F" sketch:type="MSShapeGroup"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,113 @@
/*
* Template Diff plugin
*
* Data attributes:
* - data-plugin="template-diff" - enables the plugin on an element
*
* JavaScript API:
* $('pre').templateDiff({ option: 'value' })
*
* Dependences:
* - jsdiff (diff.js)
*/
+function ($) { "use strict";
// TEMPALTE DIFF CLASS DEFINITION
// ============================
var TemplateDiff = function(element, options) {
this.options = options
this.$el = $(element)
// Init
this.init()
}
TemplateDiff.DEFAULTS = {
oldFieldName: null,
newFieldName: null,
contentTag: '',
diffType: 'lines' // chars, words, lines
}
TemplateDiff.prototype.init = function() {
var
oldValue = $('[data-field-name="'+this.options.oldFieldName+'"] .form-control '+this.options.contentTag).html(),
newValue = $('[data-field-name="'+this.options.newFieldName+'"] .form-control '+this.options.contentTag).html()
oldValue = $('<div />').html(oldValue).text()
newValue = $('<div />').html(newValue).text()
this.diffStrings(oldValue, newValue)
}
TemplateDiff.prototype.diffStrings = function(oldValue, newValue) {
var result = this.$el.get(0)
var diffType = 'diff' + this.options.diffType[0].toUpperCase() + this.options.diffType.slice(1)
var diff = JsDiff[diffType](oldValue, newValue)
var fragment = document.createDocumentFragment();
for (var i=0; i < diff.length; i++) {
if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
var swap = diff[i];
diff[i] = diff[i + 1];
diff[i + 1] = swap;
}
var node;
if (diff[i].removed) {
node = document.createElement('del');
node.appendChild(document.createTextNode(diff[i].value));
}
else if (diff[i].added) {
node = document.createElement('ins');
node.appendChild(document.createTextNode(diff[i].value));
}
else {
node = document.createTextNode(diff[i].value);
}
fragment.appendChild(node);
}
result.textContent = '';
result.appendChild(fragment);
}
// TEMPALTE DIFF PLUGIN DEFINITION
// ============================
var old = $.fn.templateDiff
$.fn.templateDiff = function (option) {
var args = Array.prototype.slice.call(arguments, 1), result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.example')
var options = $.extend({}, TemplateDiff.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.example', (data = new TemplateDiff(this, options)))
if (typeof option == 'string') result = data[option].apply(data, args)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.templateDiff.Constructor = TemplateDiff
// TEMPALTE DIFF NO CONFLICT
// =================
$.fn.templateDiff.noConflict = function () {
$.fn.templateDiff = old
return this
}
// TEMPALTE DIFF DATA-API
// ===============
$(document).render(function () {
$('[data-plugin="template-diff"]').templateDiff()
});
}(window.jQuery);

View File

@@ -0,0 +1,701 @@
/*
* Scripts for the CMS page.
*/
+function ($) { "use strict";
var Base = $.wn.foundation.base,
BaseProto = Base.prototype
var CmsPage = function() {
Base.call(this)
//
// Initialization
//
this.init()
this.widgets = Snowboard['backend.ui.widgetHandler']()
}
CmsPage.prototype = Object.create(BaseProto)
CmsPage.prototype.constructor = CmsPage
CmsPage.prototype.init = function() {
$(document).ready(this.proxy(this.registerHandlers))
}
CmsPage.prototype.updateTemplateList = function(type) {
var $form = $('#cms-side-panel form[data-template-type='+type+']'),
templateList = type + 'List'
$form.request(templateList + '::onUpdate', {
complete: function() {
$('button[data-control=delete-template]', $form).trigger('oc.triggerOn.update')
}
})
}
CmsPage.prototype.registerHandlers = function() {
var $document = $(document),
$masterTabs = $('#cms-master-tabs')
$masterTabs.on('closed.oc.tab', this.proxy(this.onTabClosed))
$masterTabs.on('beforeClose.oc.tab', this.proxy(this.onBeforeTabClose))
$masterTabs.on('oc.beforeRequest', this.proxy(this.onBeforeRequest))
$masterTabs.on('shown.bs.tab', this.proxy(this.onTabShown))
$masterTabs.on('initTab.oc.tab', this.proxy(this.onInitTab))
$masterTabs.on('afterAllClosed.oc.tab', this.proxy(this.onAfterAllTabsClosed))
$(window).on('ajaxInvalidField', this.proxy(this.ajaxInvalidField))
$document.on('open.oc.list', '#cms-side-panel', this.proxy(this.onOpenDocument))
$document.on('ajaxUpdate', '[data-control=filelist], [data-control=assetlist]', this.proxy(this.onAjaxUpdate))
$document.on('ajaxError', '#cms-master-tabs form', this.proxy(this.onAjaxError))
$document.on('ajaxSuccess', '#cms-master-tabs form', this.proxy(this.onAjaxSuccess))
$document.on('click', '#cms-side-panel form button[data-control=create-template], #cms-side-panel form li a[data-control=create-template]', this.proxy(this.onCreateTemplateClick))
$document.on('click', '#cms-side-panel form button[data-control=delete-template]', this.proxy(this.onDeleteTemplateClick))
$document.on('showing.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorShowing))
$document.on('hidden.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHidden))
$document.on('hiding.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHiding))
$document.on('click', '#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist a.remove', this.proxy(this.onComponentRemove))
$document.on('click', '#cms-component-list [data-component]', this.proxy(this.onComponentClick))
// Watch for PHP editors
window.Snowboard.on('backend.formwidget.codeeditor.create', this.proxy(this.onCodeEditorCreate));
}
// EVENT HANDLERS
// ============================
CmsPage.prototype.onOpenDocument = function(event) {
/*
* Open a document when it's clicked in the sidebar
*/
var $item = $(event.relatedTarget),
$form = $item.closest('[data-template-type]'),
data = {
type: $form.data('template-type'),
theme: $item.data('item-theme'),
path: $item.data('item-path')
},
tabId = data.type + '-' + data.theme + '-' + data.path
if (data.type == 'asset' && $item.data('editable') === undefined)
return true
if ($form.length == 0)
return false
/*
* Find if the tab is already opened
*/
if ($('#cms-master-tabs').data('oc.tab').goTo(tabId))
return false
/*
* Open a new tab
*/
$.wn.stripeLoadIndicator.show()
$form.request('onOpenTemplate', {
data: data
}).done(function(data) {
$.wn.stripeLoadIndicator.hide()
$('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon'))
}).always(function() {
$.wn.stripeLoadIndicator.hide()
}).fail(function(jqXHR, textStatus, errorThrown) {
$.wn.stripeLoadIndicator.hide()
})
return false
}
CmsPage.prototype.ajaxInvalidField = function(ev, element, name, messages, isFirst) {
/*
* Detect invalid fields, uncollapse the panel
*/
if (!isFirst)
return
ev.preventDefault()
var $el = $(element),
$panel = $el.closest('.form-tabless-fields.collapsed'),
$primaryPanel = $el.closest('.control-tabs.primary-tabs.collapsed')
if ($panel.length > 0)
$panel.removeClass('collapsed')
if ($primaryPanel.length > 0) {
$primaryPanel.removeClass('collapsed')
var pane = $primaryPanel.closest('.tab-pane'),
$secondaryPanel = $('.control-tabs.secondary-tabs', pane)
$secondaryPanel.removeClass('primary-collapsed')
}
$el.focus()
}
CmsPage.prototype.onTabClosed = function(ev) {
this.updateModifiedCounter()
if ($('> div.tab-content > div.tab-pane', '#cms-master-tabs').length == 0)
this.setPageTitle('')
}
CmsPage.prototype.onBeforeTabClose = function(ev) {
if ($.fn.table !== undefined)
$('[data-control=table]', ev.relatedTarget).table('dispose')
$.wn.foundation.controlUtils.disposeControls(ev.relatedTarget.get(0))
}
CmsPage.prototype.onBeforeRequest = function(ev) {
var $form = $(ev.target)
if ($('.components .layout-cell.error-component', $form).length > 0) {
if (!confirm('The form contains unknown components. Their properties will be lost on save. Do you want to save the form?'))
ev.preventDefault()
}
}
CmsPage.prototype.onTabShown = function(ev) {
/*
* Listen for the tabs "shown" event to track the current template in the list
*/
var $target = $(ev.target)
if ($target.closest('[data-control=tab]').attr('id') != 'cms-master-tabs')
return
var dataId = $target.closest('li').attr('data-tab-id'),
title = $target.attr('title'),
$sidePanel = $('#cms-side-panel')
if (title)
this.setPageTitle(title)
$sidePanel.find('[data-control=filelist]').fileList('markActive', dataId)
$sidePanel.find('form').trigger('oc.list.setActiveItem', [dataId])
}
CmsPage.prototype.onInitTab = function(ev, data) {
/*
* Listen for the tabs "initTab" event to inject extra controls to the tab
*/
if ($(ev.target).attr('id') != 'cms-master-tabs')
return
var $collapseIcon = $('<a href="javascript:;" class="tab-collapse-icon tabless"><i class="icon-chevron-up"></i></a>'),
$panel = $('.form-tabless-fields', data.pane)
$panel.append($collapseIcon);
$collapseIcon.click(function(){
$panel.toggleClass('collapsed')
if (typeof(localStorage) !== 'undefined')
localStorage.ocCmsTablessCollapsed = $panel.hasClass('collapsed') ? 1 : 0
window.setTimeout(function(){
$(window).trigger('oc.updateUi')
}, 500)
return false
})
var $primaryCollapseIcon = $('<a href="javascript:;" class="tab-collapse-icon primary"><i class="icon-chevron-down"></i></a>'),
$primaryPanel = $('.control-tabs.primary-tabs', data.pane),
$secondaryPanel = $('.control-tabs.secondary-tabs', data.pane)
if ($primaryPanel.length > 0) {
$secondaryPanel.append($primaryCollapseIcon);
$primaryCollapseIcon.click(function(){
$primaryPanel.toggleClass('collapsed')
$secondaryPanel.toggleClass('primary-collapsed')
$(window).trigger('oc.updateUi')
if (typeof(localStorage) !== 'undefined')
localStorage.ocCmsPrimaryCollapsed = $primaryPanel.hasClass('collapsed') ? 1 : 0
return false
})
}
if (typeof(localStorage) !== 'undefined') {
if (!$('a', data.tab).hasClass('new-template') && localStorage.ocCmsTablessCollapsed == 1)
$panel.addClass('collapsed')
if (localStorage.ocCmsPrimaryCollapsed == 1) {
$primaryPanel.addClass('collapsed')
$secondaryPanel.addClass('primary-collapsed')
}
}
var $componentListFormGroup = $('.control-componentlist', data.pane).closest('.form-group')
if ($primaryPanel.length > 0)
$primaryPanel.before($componentListFormGroup)
else
$secondaryPanel.parent().before($componentListFormGroup)
$componentListFormGroup.removeClass()
$componentListFormGroup.addClass('layout-row min-size')
this.updateComponentListClass(data.pane)
var $form = $('form', data.pane),
self = this
$form.on('changed.oc.changeMonitor', function() {
$panel.trigger('modified.oc.tab')
$panel.find('[data-control=commit-button]').addClass('hide');
$panel.find('[data-control=reset-button]').addClass('hide');
self.updateModifiedCounter()
})
$form.on('unchanged.oc.changeMonitor', function() {
$panel.trigger('unmodified.oc.tab')
self.updateModifiedCounter()
})
}
CmsPage.prototype.onCodeEditorCreate = function (widget, editor) {
const $form = $(widget.element.closest('form'));
if (widget.config.get('language') === 'php') {
let value = widget.getValue();
// If no PHP tag at the start, prepend one
if (!/^<\?php\s*/.test(value)) {
widget.setValue('<?php\n' + value);
}
// Verify the editor has at least 2 lines before hiding line 1
const lineCount = widget.getModel().getLineCount();
if (lineCount >= 2) {
// Only hide line 1 if it contains just the PHP open tag
const firstLine = widget.getModel().getLineContent(1);
if (/^<\?php\s*$/.test(firstLine)) {
widget.fromLine(2);
}
}
}
// Add codelens and action to customise component templates
const templateCommand = editor.addCommand(
0,
function (command, range, name) {
$form.request('onExpandMarkupToken', {
data: {
tokenType: 'component',
tokenName: name,
},
success: function (data) {
if (data.result) {
widget.replace(range, data.result)
}
},
});
},
);
widget.addCodeLens(
'twig',
function (model, token) {
// Find component tags
const lenses = [];
const matches = model.findMatches('\\{%\\scomponent\\s[\'"]([^\'"]+)[\'"][^%]*\\s%\\}', true, true, false, null, true);
matches.forEach((match) => {
const name = match.matches[1] ?? 'unknown';
lenses.push({
range: match.range,
id: 'component-' + name + '-lens',
command: {
title: 'Customize template',
id: templateCommand,
arguments: [match.range, name]
},
});
});
return {
lenses: lenses,
dispose: function () {
}
}
}
);
}
CmsPage.prototype.onAfterAllTabsClosed = function(ev) {
var $sidePanel = $('#cms-side-panel')
$sidePanel.find('[data-control=filelist]').fileList('markActive', null)
$sidePanel.find('form').trigger('oc.list.setActiveItem', [null])
}
CmsPage.prototype.onAjaxUpdate = function(ev) {
var dataId = $('#cms-master-tabs .nav-tabs li.active').attr('data-tab-id'),
$sidePanel = $('#cms-side-panel')
$sidePanel.find('[data-control=filelist]').fileList('markActive', dataId)
$sidePanel.find('form').trigger('oc.list.setActiveItem', [dataId])
}
CmsPage.prototype.onAjaxSuccess = function(ev, context, data) {
var element = ev.target
// Update the visibilities of the commit & reset buttons
$('[data-control=commit-button]', element).toggleClass('hide', !data.canCommit)
$('[data-control=reset-button]', element).toggleClass('hide', !data.canReset)
if (data.templatePath !== undefined) {
$('input[name=templatePath]', element).val(data.templatePath)
$('input[name=templateMtime]', element).val(data.templateMtime)
$('[data-control=delete-button]', element).removeClass('hide')
$('[data-control=preview-button]', element).removeClass('hide')
if (data.pageUrl !== undefined)
$('[data-control=preview-button]', element).attr('href', data.pageUrl)
}
if (data.tabTitle !== undefined) {
$('#cms-master-tabs').ocTab('updateTitle', $(element).closest('.tab-pane'), data.tabTitle)
this.setPageTitle(data.tabTitle)
}
var tabId = $('input[name=templateType]', element).val() + '-'
+ $('input[name=theme]', element).val() + '-'
+ $('input[name=templatePath]', element).val();
$('#cms-master-tabs').ocTab('updateIdentifier', $(element).closest('.tab-pane'), tabId)
var templateType = $('input[name=templateType]', element).val()
if (templateType.length > 0) {
$.wn.cmsPage.updateTemplateList(templateType)
if (templateType == 'layout')
this.updateLayouts(element)
}
if (context.handler == 'onSave' && (!data['X_WINTER_ERROR_FIELDS'] && !data['X_WINTER_ERROR_MESSAGE'])) {
$(element).trigger('unchange.oc.changeMonitor')
}
// Reload the form if the server has requested it
if (data.forceReload) {
this.reloadForm(element)
}
}
CmsPage.prototype.onAjaxError = function(ev, context, message, data, jqXHR) {
if (context.handler == 'onSave') {
if (jqXHR.responseText == 'mtime-mismatch') {
ev.preventDefault()
this.handleMtimeMismatch(ev.target)
}
}
}
CmsPage.prototype.onCreateTemplateClick = function(ev) {
var $form = $(ev.target).closest('[data-template-type]'),
type = $form.data('template-type'),
tabId = type + Math.random(),
self = this
$.wn.stripeLoadIndicator.show()
$form.request('onCreateTemplate', {
data: {type: type}
}).done(function(data) {
$('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon') + ' new-template')
$('#layout-side-panel').trigger('close.oc.sidePanel')
self.setPageTitle(data.tabTitle)
}).always(function(){
$.wn.stripeLoadIndicator.hide()
})
}
CmsPage.prototype.onDeleteTemplateClick = function(ev) {
var $el = $(ev.currentTarget),
$form = $el.closest('form'),
templateType = $form.data('template-type'),
self = this
if (!confirm($el.data('confirmation')))
return
$.wn.stripeLoadIndicator.show()
$form.request('onDeleteTemplates', {
data: {type: templateType}
}).done(function(data) {
var tabs = $('#cms-master-tabs').data('oc.tab');
$.each(data.deleted, function(index, path){
var
tabId = templateType + '-' + data.theme + '-' + path,
tab = tabs.findByIdentifier(tabId)
$('#cms-master-tabs').ocTab('closeTab', tab, true)
})
if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length)
$.wn.flashMsg({text: data.error, 'class': 'error'})
}).always(function(){
self.updateTemplateList(templateType)
$.wn.stripeLoadIndicator.hide()
})
}
CmsPage.prototype.onInspectorShowing = function(ev, data) {
var $dragScroll = $(ev.currentTarget).closest('[data-control="toolbar"]').data('oc.dragScroll')
if ($dragScroll) {
$dragScroll.goToElement(ev.currentTarget, data.callback)
} else {
data.callback();
}
ev.stopPropagation()
}
CmsPage.prototype.onInspectorHidden = function(ev) {
var element = ev.target,
values = JSON.parse($('[data-inspector-values]', element).val())
$('[name="component_aliases[]"]', element).val(values['oc.alias'])
$('span.alias', element).text(values['oc.alias'])
}
CmsPage.prototype.onInspectorHiding = function(ev, values) {
var element = ev.target,
values = JSON.parse($('[data-inspector-values]', element).val()),
alias = values['oc.alias'],
$componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout'),
$cell = $(ev.target).parent()
$('div.layout-cell', $componentList).each(function(){
if ($cell.get(0) == this)
return true
var $input = $('input[name="component_aliases[]"]', this)
if ($input.val() == alias) {
ev.preventDefault()
alert('The component alias "'+alias+'" is already used.')
return false
}
})
}
CmsPage.prototype.onComponentRemove = function(ev) {
var element = ev.currentTarget
$(element).trigger('change')
var pane = $(element).closest('.tab-pane'),
component = $(element).closest('div.layout-cell')
/*
* Remove any {% component %} tags in the editor for this component
*/
var editor = $('[data-control=codeeditor]', pane)
if (editor.length) {
var alias = $('input[name="component_aliases[]"]', component).val().replace(/^@/, ''),
codeEditor = this.widgets.getWidget(editor.get(0))
codeEditor.replace(new RegExp('\\{% +component +\'' + alias + '\'.*?%\\}'), '');
}
component.remove()
$(window).trigger('oc.updateUi')
this.updateComponentListClass(pane)
return false
}
CmsPage.prototype.onComponentClick = function(ev) {
/*
* Determine if a page or layout is open in the master tabs
*/
var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout')
if ($componentList.length == 0) {
alert('Components can be added only to pages, partials and layouts.')
return;
}
var $component = $(ev.currentTarget).clone(),
$iconInput = $component.find('[data-component-icon]'),
$componentContainer = $('.layout-relative', $component),
$configInput = $component.find('[data-inspector-config]'),
$aliasInput = $component.find('[data-component-default-alias]'),
$valuesInput = $component.find('[data-inspector-values]'),
$nameInput = $component.find('[data-component-name]'),
$classInput = $component.find('[data-inspector-class]'),
alias = $aliasInput.val(),
originalAlias = alias,
counter = 2,
existingAliases = []
$('div.layout-cell input[name="component_aliases[]"]', $componentList).each(function(){
existingAliases.push($(this).val())
})
while($.inArray(alias, existingAliases) !== -1) {
alias = originalAlias + counter
counter++
}
// Set the last alias used so dragComponents can use it
$('input[name="component_aliases[]"]', $(ev.currentTarget)).val(alias)
$component.attr('data-component-attached', true)
$componentContainer.addClass($iconInput.val())
$iconInput.remove()
$componentContainer.attr({
'data-inspectable': '',
'data-inspector-title': $component.find('span.name').text(),
'data-inspector-description': $component.find('span.description').text(),
'data-inspector-config': $configInput.val(),
'data-inspector-class': $classInput.val()
})
$configInput.remove()
$('input[name="component_names[]"]', $component).val($nameInput.val())
$nameInput.remove()
$('input[name="component_aliases[]"]', $component).val(alias)
$component.find('span.alias').text(alias)
$valuesInput.val($valuesInput.val().replace('--alias--', alias))
$aliasInput.remove()
$component.addClass('adding')
$componentList.append($component)
$componentList.closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement($component)
$component.removeClass('adding')
$component.trigger('change')
this.updateComponentListClass($component.closest('.tab-pane'))
$(window).trigger('oc.updateUi')
}
// INTERNAL METHODS
// ============================
CmsPage.prototype.updateComponentListClass = function(pane) {
var $componentList = $('.control-componentlist', pane),
$primaryPanel = $('.control-tabs.primary-tabs', pane),
$primaryTabContainer = $('.nav-tabs', $primaryPanel),
hasComponents = $('.layout', $componentList).children(':not(.hidden)').length > 0
$primaryTabContainer.toggleClass('component-area', hasComponents)
$componentList.toggleClass('has-components', hasComponents)
}
CmsPage.prototype.updateModifiedCounter = function() {
var counters = {
page: { menu: 'pages', count: 0 },
partial: { menu: 'partials', count: 0 },
layout: { menu: 'layouts', count: 0 },
content: { menu: 'content', count: 0 },
asset: { menu: 'assets', count: 0}
}
$('> div.tab-content > div.tab-pane[data-modified]', '#cms-master-tabs').each(function(){
var inputType = $('> form > input[name=templateType]', this).val()
counters[inputType].count++
})
$.each(counters, function(type, data){
$.wn.sideNav.setCounter('cms/' + data.menu, data.count);
})
}
CmsPage.prototype.handleMtimeMismatch = function(form) {
var $form = $(form)
$form.popup({ handler: 'onOpenConcurrencyResolveForm' })
var popup = $form.data('oc.popup'),
self = this
$(popup.$content).on('click', 'button[data-action=reload]', function(){
popup.hide()
self.reloadForm(form)
})
$(popup.$content).on('click', 'button[data-action=save]', function(){
popup.hide()
$('input[name=templateForceSave]', $form).val(1)
$('a[data-request=onSave]', $form).trigger('click')
$('input[name=templateForceSave]', $form).val(0)
})
}
CmsPage.prototype.reloadForm = function(form) {
var
$form = $(form),
data = {
type: $('[name=templateType]', $form).val(),
theme: $('[name=theme]', $form).val(),
path: $('[name=templatePath]', $form).val(),
},
tabId = data.type + '-' + data.theme + '-' + data.path,
tabs = $('#cms-master-tabs').data('oc.tab'),
tab = tabs.findByIdentifier(tabId),
self = this
/*
* Update tab
*/
$.wn.stripeLoadIndicator.show()
$form.request('onOpenTemplate', {
data: data
}).done(function(data) {
$('#cms-master-tabs').ocTab('updateTab', tab, data.tabTitle, data.tab)
$('#cms-master-tabs').ocTab('unmodifyTab', tab)
self.updateModifiedCounter()
}).always(function() {
$.wn.stripeLoadIndicator.hide()
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
})
}
CmsPage.prototype.setPageTitle = function(title) {
if (title.length)
$.wn.layout.setPageTitle(title + ' | ')
else
$.wn.layout.setPageTitle(title)
}
CmsPage.prototype.updateLayouts = function(form) {
$(form).request('onGetTemplateList', {
success: function(data) {
$('#cms-master-tabs > .tab-content select[name="settings[layout]"]').each(function(){
var
$select = $(this),
value = $select.val()
$select.find('option').remove()
$.each(data.layouts, function(layoutFile, layoutName){
$select.append($('<option>').attr('value', layoutFile).text(layoutName))
})
$select.trigger('pause.oc.changeMonitor')
$select.val(value)
$select.trigger('change')
$select.trigger('resume.oc.changeMonitor')
})
}
})
}
$.wn.cmsPage = new CmsPage();
}(window.jQuery);

View File

@@ -0,0 +1,260 @@
/*
* DragComponents plugin
*
* Data attributes:
* - data-control="dragcomponents" - enables the plugin on an element
* - data-option="value" - an option with a value
*
* JavaScript API:
* $('a#someElement').dragComponents({ option: 'value' })
*
* Dependences:
* - Some other plugin (filename.js)
*/
+function ($) { "use strict";
// DRAGCOMPONENTS CLASS DEFINITION
// ============================
var DragComponents = function(element, options) {
var self = this
this.options = options
this.$el = $(element)
var $el = this.$el,
widgets = Snowboard['backend.ui.widgetHandler'](),
$clone,
$editorArea,
$editor,
$componentList,
adjX = 0,
adjY = 0,
dragging = false,
startPos,
editorPos
$el.mousedown(function(event){
if ($el.data('component-attached')) return
startDrag(event)
return false
})
$el.on('touchstart', function(event){
if ($el.data('component-attached')) return
var touchEvent = event.originalEvent;
if (touchEvent.touches.length == 1) {
startDrag(touchEvent.touches[0])
event.stopPropagation()
}
})
function initDrag(event) {
$componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist')
$el.addClass(self.options.placeholderClass)
$clone.show()
$editorArea = $('#cms-master-tabs > div.tab-content > .tab-pane.active [data-control="codeeditor"]')
if (!$editorArea.length) {
return
}
$editor = widgets.getWidget($editorArea.get(0));
if (!$editor || !$editor.getEditor()) return;
$editor.getEditor().focus();
editorPos = $editor.getEditor().onMouseMove((event) => {
if (event.target && event.target.position) {
$editor.getEditor().setPosition(event.target.position);
}
});
}
/*
* Internal event, drag has started
*/
function startDrag(event) {
startPos = $el.offset()
$clone = $el.clone().appendTo($(document.body))
$clone
.css({
zIndex: '99999',
position: 'absolute',
pointerEvents: 'none'
})
.addClass('draggable-component-item')
.width($el.width())
.height($el.height())
.hide()
var objX = (event.pageX - startPos.left),
objY = (event.pageY - startPos.top)
$clone.data('dragComponents', { x: objX, y: objY })
if (Modernizr.touchevents) {
$(window).on('touchmove.oc.dragcomponents', function(event){
var touchEvent = event.originalEvent
moveDrag(touchEvent.touches[0])
event.preventDefault()
})
$(window).on('touchend.oc.dragcomponents', function(event) {
stopDrag()
})
}
else {
$(window).on('mousemove.oc.dragcomponents', function(event){
moveDrag(event)
$(document.body).addClass(self.options.dragClass)
return false
})
$(window).on('mouseup.oc.dragcomponents', function(mouseUpEvent){
var isClick = event.pageX == mouseUpEvent.pageX && event.pageY == mouseUpEvent.pageY
stopDrag(isClick)
return false
})
}
}
/*
* Internal event, drag is active
*/
function moveDrag(event) {
if (!dragging) {
dragging = true
initDrag(event)
}
adjY = $(window).scrollTop()
adjX = $(window).scrollLeft()
var offset = $clone.data('dragComponents')
$clone.css({
left: (event.pageX - adjX - offset.x),
top: (event.pageY - adjY - offset.y)
})
if (collision($clone, $componentList))
$componentList.addClass('droppable')
else
$componentList.removeClass('droppable')
}
/*
* Internal event, drag has ended
*/
function stopDrag(click) {
dragging = false
if (click)
$(document.body).removeClass(self.options.dragClass)
$el.removeClass(self.options.placeholderClass)
$(window)
.off('mousemove.oc.dragcomponents mouseup.oc.dragcomponents')
.removeData('dragComponents')
if (!click)
finishDrag()
$clone.remove()
window.setTimeout(function(){
if (!click) {
$(document.body).removeClass(self.options.dragClass)
}
}, 100)
}
function finishDrag() {
// Dragged to the code editor
if (collision($clone, $editorArea)) {
// Add the component to the page
$el.click()
// Can only attach to page or layouts
if ($componentList.length && $editor && $editor.getEditor()) {
// Inject {% component %} tag
var alias = $('input[name="component_aliases[]"]', $el).val()
$editor.insert("{% component '" + alias + "' %}")
}
}
// Dragged to the component list
else if (collision($clone, $componentList)) {
// Add the component to the page
$el.click()
}
if (editorPos) {
editorPos.dispose();
}
if ($componentList.length) {
$componentList.removeClass('droppable')
}
}
function collision($div1, $div2) {
if (!$div1 || !$div2 || !$div1.length || !$div2.length)
return false
var x1 = $div1.offset().left,
y1 = $div1.offset().top,
h1 = $div1.outerHeight(true),
w1 = $div1.outerWidth(true),
b1 = y1 + h1,
r1 = x1 + w1,
x2 = $div2.offset().left,
y2 = $div2.offset().top,
h2 = $div2.outerHeight(true),
w2 = $div2.outerWidth(true),
b2 = y2 + h2,
r2 = x2 + w2
return !(b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2)
}
}
DragComponents.DEFAULTS = {
dragClass: 'drag',
placeholderClass: 'placeholder'
}
// DRAGCOMPONENTS PLUGIN DEFINITION
// ============================
var old = $.fn.dragComponents
$.fn.dragComponents = function (option) {
var args = Array.prototype.slice.call(arguments, 1)
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.dragcomponents')
var options = $.extend({}, DragComponents.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.dragcomponents', (data = new DragComponents(this, options)))
else if (typeof option == 'string') data[option].apply(data, args)
})
}
$.fn.dragComponents.Constructor = DragComponents
// DRAGCOMPONENTS NO CONFLICT
// =================
$.fn.dragComponents.noConflict = function () {
$.fn.dragComponents = old
return this
}
// DRAGCOMPONENTS DATA-API
// ===============
$(document).on('mouseenter.oc.dragcomponents', '[data-control="dragcomponent"]', function() {
$(this).dragComponents()
});
}(window.jQuery);

View File

@@ -0,0 +1,183 @@
/*
* Token Expander plugin
* Locates Twig tokens and replaces them with potential content inside.
*
* JavaScript API:
* $('#codeEditor').tokenExpander({ option: 'value' })
*
* Dependences:
* - Code Edtior (codeeditor.js)
*/
+function ($) { "use strict";
// TOKEN EXPANDER CLASS DEFINITION
// ============================
var TokenExpander = function(element, options) {
this.options = options
this.$el = $(element)
// Public properties
this.something = false
// Init
this.init()
}
TokenExpander.DEFAULTS = {
option: 'default'
}
TokenExpander.prototype.init = function() {
this.$editor = this.$el.codeEditor('getEditorObject')
this.$selection = this.$editor.getSelection()
this.$session = this.$editor.getSession()
this.tokenName = null
this.tokenValue = null
this.tokenDefinition = null
this.tokenRange = null
this.$selection.on('changeCursor', $.proxy(this.cursorChange, this))
}
TokenExpander.prototype.cursorChange = function(event) {
var cursor = this.$selection.getCursor(),
word = this.getActiveWord(cursor).toLowerCase()
if (word == 'component') {
this.handleCursorOnToken(cursor, word)
}
else if (this.tokenName) {
this.tokenName = null
this.tokenValue = null
this.tokenDefinition = null
this.tokenRange = null
this.$el.trigger('hide.oc.tokenexpander')
}
}
TokenExpander.prototype.handleCursorOnToken = function(cursor, token) {
var line = this.$session.getLine(cursor.row),
definition = this.getTwigTokenDefinition(token, line, cursor.column)
if (definition) {
var value = this.getTwigTokenValue(token, definition[0])
if (value) {
if (!this.tokenName)
this.$el.trigger('show.oc.tokenexpander')
this.tokenName = token
this.tokenValue = value
this.tokenDefinition = definition
this.tokenRange = this.$selection.getRange() // Used only for its row
}
}
}
/**
* Callback must return a promise object
*/
TokenExpander.prototype.expandToken = function(callback) {
var $editor = this.$editor,
$session = this.$session,
definition = this.tokenDefinition,
range = this.tokenRange
$editor.setReadOnly(true)
callback(this.tokenName, this.tokenValue)
.done(function(data){
range.setStart(range.start.row, definition[1])
range.setEnd(range.end.row, definition[2])
$session.replace(range, data.result)
})
.always(function(){
$editor.setReadOnly(false)
})
}
TokenExpander.prototype.getTwigTokenValue = function(tokenName, tokenString) {
var regex = new RegExp("^{%\\s*"+tokenName+"\\s(['"+'"'+"])([^"+'"'+"']+)(?:\\1)[^(?:%})]+%}$", "i"),
regexMatch = regex.exec(tokenString)
if (regexMatch && regexMatch[2])
return regexMatch[2]
return null
}
/**
* Returns an array of [tokenString, startPos, endPos] or null
* Eg: ['{% component "thing" %}', 0, 23]
*/
TokenExpander.prototype.getTwigTokenDefinition = function(token, str, pos, filter) {
if (!filter)
filter = 0
var filteredStr = str.substring(filter),
regex = new RegExp("{%\\s*"+token+"\\s[^(?:%})]+%}", "i"),
regexMatch = regex.exec(filteredStr)
if (regexMatch) {
var start = str.indexOf(regexMatch[0], filter),
end = start + regexMatch[0].length
// Win!
if (start < pos && end > pos)
return [regexMatch[0], start, end]
// Try again
return this.getTwigTokenDefinition(token, str, pos, end)
}
// Fail
return null
}
TokenExpander.prototype.getActiveWord = function(cursor) {
var $session = this.$session,
wordRange = $session.getWordRange(cursor.row, cursor.column),
word = $session.getTextRange(wordRange)
return word
}
// TOKEN EXPANDER PLUGIN DEFINITION
// ============================
var old = $.fn.tokenExpander
$.fn.tokenExpander = function (option) {
var args = Array.prototype.slice.call(arguments, 1), regexMatch
this.each(function () {
var $this = $(this)
var data = $this.data('oc.tokenexpander')
var options = $.extend({}, TokenExpander.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.tokenexpander', (data = new TokenExpander(this, options)))
if (typeof option == 'string') regexMatch = data[option].apply(data, args)
if (typeof regexMatch != 'undefined') return false
})
return regexMatch ? regexMatch : this
}
$.fn.tokenExpander.Constructor = TokenExpander
// TOKEN EXPANDER NO CONFLICT
// =================
$.fn.tokenExpander.noConflict = function () {
$.fn.tokenExpander = old
return this
}
}(window.jQuery);

View File

@@ -0,0 +1,406 @@
@import "../../../backend/assets/less/core/boot.less";
//
// Component list
// --------------------------------------------------
@color-component-list-bg: @brand-secondary;
@color-component-bg: #ffffff;
@color-component-text: #475354;
@color-component-hover-bg: @brand-accent;
@color-component-hover-text: #ffffff;
@color-component-placeholder: #e0e0e0;
@color-group-bg: #f1f3f4;
@color-error-component-bg: @brand-danger;
@color-error-component-text: #ffffff;
@color-warning-component-bg: #ffc107;
@color-warning-component-text: #343a40;
.component-lego-icon() {
position: absolute;
font-size: 37px;
top: 1px;
z-index: 50;
color: @color-component-bg;
text-shadow: 0 0 1px @color-component-text;
width: 12px;
overflow: hidden;
text-indent: -25px;
right: -12px;
.icon(@puzzle-piece);
}
.draggable-component-item,
.component-list .components div.layout-cell,
div.control-componentlist div.components div.layout-cell {
font-size: 11px;
cursor: pointer;
background: @color-component-bg;
.user-select(none);
&:hover {
background: @color-component-hover-bg;
}
> div {
white-space: normal;
color: @color-component-text;
position: relative;
border-right: 1px solid @color-panel-light;
&:before {
position: absolute;
font-size: 16px;
left: 15px;
top: 7px;
.opacity(0.7);
}
&:hover {
color: @color-component-hover-text;
&:before {
.opacity(1);
}
}
&:after {
.component-lego-icon();
}
&:hover:after {
text-shadow: none;
color: @color-component-hover-bg;
}
span {
display: block;
&.name {
white-space: nowrap;
padding: 8px 15px 0;
font-weight: 400;
line-height: 20px;
font-size: @font-size-base - 1;
}
&.description {
padding: 0 15px 10px;
margin-top: 8px;
font-weight: 400;
font-size: @font-size-base - 3;
line-height: 150%;
}
}
}
&.placeholder > div {
background: @color-component-placeholder;
color: @color-component-placeholder;
&:before, &:after {
color: @color-component-placeholder !important;
text-shadow: none !important;
}
}
}
// Reduce the double standard padding, since the tabs have padding
// at the top and the components field has padding at the bottom.
[data-field-name="components"] + .control-tabs.primary-tabs {
margin-top: -(@padding-standard / 2);
}
div.control-componentlist {
position: relative;
padding: 0;
.transition(all 0.3s ease);
&.droppable {
background-color: lighten(@color-component-list-bg, 20%);
}
&.has-components {
padding: 0 @padding-standard @padding-standard;
}
div.layout {
width: auto;
}
div.components {
div.layout-cell {
&.error-component {
background: @color-error-component-bg;
> div {
color: @color-error-component-text;
&:after {
color: @color-error-component-bg;
}
}
}
&.warning-component {
background: @color-warning-component-bg;
> div {
color: @color-warning-component-text;
&:after {
color: @color-warning-component-bg;
}
}
}
&:first-child {
.border-left-radius(3px);
}
&:last-child {
margin-right: 0;
.border-right-radius(3px);
> div {
border-right: none;
&:after {
display: none;
}
}
}
&:nth-child(2n) > div:after {
top: auto;
bottom: 5px;
}
&:first-child {
> div.popover-highlight {
.border-left-radius(3px);
}
}
&:last-child {
> div.popover-highlight {
.border-right-radius(3px);
}
}
> div {
.translate(0, 0);
.transition-transform(0.2s);
max-width: 250px;
min-width: 170px;
&.popover-highlight {
border-right-color: rgba(0,0,0,0);
background: @color-component-bg!important;
color: @color-component-text!important;
&:before {
.opacity(0.7);
}
&:after {
color: @color-component-bg;
text-shadow: none;
}
a.remove {
display: none;
}
}
span {
&.name {
padding-left: 38px;
}
&.description {
padding-bottom: 35px;
}
&.alias {
padding: 0 15px;
font-weight: 500;
position: absolute;
width: 100%;
bottom: 10px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
&:before {
margin-right: 4px;
.opacity(0.55);
}
}
}
a.remove {
position: absolute;
display: inline-block;
top: 1px;
right: 5px;
color: @close-color;
font-size: 17px;
font-weight: @close-font-weight;
.opacity(.3);
&:hover {
.opacity(.5);
text-decoration: none;
}
}
}
&.adding > div {
.translate(0, -100px)!important;
}
}
}
}
.draggable-component-item {
.opacity(.6);
span.alias { display: none; }
a.remove { display: none; }
}
.component-list .components {
div.layout {
div.layout-row {
div.layout-cell {
border-top: 1px solid @color-panel-light;
span.alias { display: none; }
a.remove { display: none; }
&:last-child > div {
border-right: none;
&:after {
display: none;
}
}
> div {
&:before {
.component-lego-icon();
.rotate(-90deg);
.opacity(1);
left: auto;
top: -17px;
right: 15px;
}
&:hover:before {
text-shadow: none;
color: @color-component-hover-bg;
}
}
}
&:first-child {
div.layout-cell {
> div {
&:before {display: none;}
}
}
}
}
}
div.layout.single {
div.layout-row > div.layout-cell > div {
border-right: none;
&:before {
display: block;
right: 55.5%;
}
}
}
}
.control-filelist.component-list {
ul li {
div.group {
background: @color-group-bg;
border-top: 1px solid darken(@color-panel-light, 2%);
padding: 10px 15px 10px 10px;
position: relative;
cursor: pointer;
h4 {
text-transform: uppercase;
font-size: 14px;
margin-top: 3px;
a {
padding-left: 33px;
&:before {
left: 0;
}
&:after {
display: none;
}
}
}
span.description {
display: block;
font-size: 13px;
padding-left: 33px;
color: @color-text-description;
}
i {
position: absolute;
left: 22px;
top: 21px;
font-size: 16px;
.opacity(0.7);
color: @color-text-title;
}
}
}
}
.disable-component-list-hover() {
background: @color-component-bg;
color: @color-component-text;
&:after {
text-shadow: 0 0 1px @color-component-text!important;
color: @color-component-bg!important;
}
}
.touch {
div.control-componentlist div.components div.layout-cell {
> div:hover, > div:active, > div:active:focus {
.disable-component-list-hover();
}
}
}
// Modifies the color of the tab background when components are present
// this is no longer required since the colors are the same! :-) -sg
// .fancy-layout {
// .control-tabs, &.control-tabs {
// &.primary-tabs {
// > div > ul.nav-tabs {
// &.component-area {
// background: @color-component-list-bg;
// }
// }
// }
// }
// }
body.drag div.control-componentlist div.components div.layout-cell {
> div, > div:hover, > div:active {
.disable-component-list-hover();
}
}

View File

@@ -0,0 +1,160 @@
@import "../../../backend/assets/less/core/boot.less";
.theme-selector-layout {
.layout-cell {
padding: 24px;
.box-sizing(border-box);
}
.theme-thumbnail {
width: 288px;
background: #ecf0f1;
border-top: 1px solid #e3e7e9;
img {
.opacity(0.6);
width: 240px;
}
}
.theme-description {
border-top: 1px solid #f2f3f4;
h3, p {
.opacity(0.6);
}
h3 {
margin: 0 0 25px 0;
font-size: 28px;
color: #2b3e50;
display: inline-block;
}
p.author {
font-size: 13px;
display: inline-block;
color: #808c8d;
}
p.description {
color: #2b3e50;
font-size: 14px;
line-height: 180%;
margin-bottom: 30px;
}
.controls {
.btn > i {
margin-right: 5px;
font-size: 16px;
position: relative;
top: 1px;
&.icon-star {
color: #f1a84e;
}
}
.dropdown {
display: inline-block;
}
}
}
.layout-row.active {
.theme-thumbnail {
background: #bdc3c7;
border-top-color: #bdc3c7;
}
.thumbnail-container {
position: relative;
&:after {
.triangle(right, 15px, 28px, #bdc3c7);
position: absolute;
right: -35px;
top: 50%;
margin-top: -14px;
}
}
}
.layout-row {
&.active, &:hover {
.theme-description {
h3, p {
.opacity(1);
}
}
.theme-thumbnail {
img {
.opacity(1);
}
}
}
&:first-child, &.links {
.theme-description, .theme-thumbnail {
border-top: none;
}
}
&.links {
.theme-thumbnail {
border-bottom: 1px solid #e3e7e9;
}
.theme-description {
border-bottom: 1px solid #f2f3f4;
}
}
}
.create-new-theme {
margin-bottom: 10px;
}
.create-new-theme,
.find-more-themes {
background: #ecf0f1;
color: #2b3e50;
text-decoration: none;
display: block;
padding: 20px;
.border-radius(4px);
&:hover {
background: @link-color;
color: white;
}
}
}
//
// Screen specific
//
@media (max-width: @screen-sm) {
.theme-selector-layout {
.layout-cell, .layout-row {
display: block!important;
width: auto!important;
height: auto!important;
}
.theme-thumbnail {
img {
width: 100%;
}
}
.layout-row.links {
.theme-thumbnail {
background: transparent;
padding: 0;
}
}
}
}

1407
modules/cms/assets/vendor/jsdiff/diff.js vendored Normal file

File diff suppressed because one or more lines are too long