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,468 @@
/*
* Code editor form field control
*
* Data attributes:
* - data-control="codeeditor" - enables the code editor plugin
* - data-vendor-path="/" - sets the path to find Ace editor files
* - data-language="php" - set the coding language used
* - data-theme="textmate" - the colour scheme and theme
*
* JavaScript API:
* $('textarea').codeEditor({ vendorPath: '/', language: 'php '})
*
* Dependancies:
* - Ace Editor (ace.js)
*/
+function ($) { "use strict";
var Base = $.wn.foundation.base,
BaseProto = Base.prototype
// CODEEDITOR CLASS DEFINITION
// ============================
var CodeEditor = function(element, options) {
Base.call(this)
this.options = options
this.$el = $(element)
this.$textarea = this.$el.find('>textarea:first')
this.$toolbar = this.$el.find('>.editor-toolbar:first')
this.$code = null
this.editor = null
this.$form = null
// Toolbar links
this.isFullscreen = false
this.$fullscreenEnable = this.$toolbar.find('li.fullscreen-enable')
this.$fullscreenDisable = this.$toolbar.find('li.fullscreen-disable')
this.isSearchbox = false
this.$searchboxEnable = this.$toolbar.find('li.searchbox-enable')
this.$searchboxDisable = this.$toolbar.find('li.searchbox-disable')
this.isReplacebox = false
this.$replaceboxEnable = this.$toolbar.find('li.replacebox-enable')
this.$replaceboxDisable = this.$toolbar.find('li.replacebox-disable')
$.wn.foundation.controlUtils.markDisposable(element)
this.init();
this.$el.trigger('oc.codeEditorReady')
}
CodeEditor.prototype = Object.create(BaseProto)
CodeEditor.prototype.constructor = CodeEditor
CodeEditor.DEFAULTS = {
fontSize: 12,
wordWrap: 'off',
codeFolding: 'manual',
autocompletion: 'manual',
tabSize: 4,
theme: 'textmate',
showInvisibles: true,
highlightActiveLine: true,
useSoftTabs: true,
autoCloseTags: true,
showGutter: true,
enableEmmet: true,
language: 'php',
margin: 0,
vendorPath: '/',
showPrintMargin: false,
highlightSelectedWord: false,
hScrollBarAlwaysVisible: false,
scrollPastEnd: 0,
readOnly: false
}
CodeEditor.prototype.init = function (){
var self = this;
/*
* Control must have an identifier
*/
if (!this.$el.attr('id')) {
this.$el.attr('id', 'element-' + Math.random().toString(36).substring(7))
}
/*
* Create code container
*/
this.$code = $('<div />')
.addClass('editor-code')
.attr('id', this.$el.attr('id') + '-code')
.css({
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0
})
.appendTo(this.$el)
/*
* Initialize ACE editor
*/
var editor = this.editor = ace.edit(this.$code.attr('id')),
options = this.options,
$form = this.$el.closest('form');
// Fixes a weird notice about scrolling
editor.$blockScrolling = Infinity
this.$form = $form
this.$textarea.hide();
editor.getSession().setValue(this.$textarea.val())
editor.on('change', this.proxy(this.onChange))
$form.on('oc.beforeRequest', this.proxy(this.onBeforeRequest))
$(window).on('resize', this.proxy(this.onResize))
$(window).on('oc.updateUi', this.proxy(this.onResize))
this.$el.one('dispose-control', this.proxy(this.dispose))
/*
* Set theme, anticipated languages should be preloaded
*/
assetManager.load({
js:[
// options.vendorPath + '/mode-' + options.language + '.js',
options.vendorPath + '/theme-' + options.theme + '.js'
]
}, function(){
editor.setTheme('ace/theme/' + options.theme)
var inline = options.language === 'php'
editor.getSession().setMode({ path: 'ace/mode/'+options.language, inline: inline })
})
/*
* Config editor
*/
editor.wrapper = this
editor.setShowInvisibles(options.showInvisibles)
editor.setBehavioursEnabled(options.autoCloseTags)
editor.setHighlightActiveLine(options.highlightActiveLine)
editor.renderer.setShowGutter(options.showGutter)
editor.renderer.setShowPrintMargin(options.showPrintMargin)
editor.setHighlightSelectedWord(options.highlightSelectedWord)
editor.renderer.setHScrollBarAlwaysVisible(options.hScrollBarAlwaysVisible)
editor.setDisplayIndentGuides(options.displayIndentGuides)
editor.getSession().setUseSoftTabs(options.useSoftTabs)
editor.getSession().setTabSize(options.tabSize)
editor.setReadOnly(options.readOnly)
editor.getSession().setFoldStyle(options.codeFolding)
editor.setFontSize(options.fontSize)
editor.on('blur', this.proxy(this.onBlur))
editor.on('focus', this.proxy(this.onFocus))
editor.setOption("scrollPastEnd", options.scrollPastEnd)
this.setWordWrap(options.wordWrap)
// Set the vendor path for Ace's require path
ace.require('ace/config').set('basePath', this.options.vendorPath)
editor.setOptions({
enableEmmet: options.enableEmmet,
enableBasicAutocompletion: options.autocompletion === 'basic',
enableSnippets: options.enableSnippets,
enableLiveAutocompletion: options.autocompletion === 'live'
})
editor.renderer.setScrollMargin(options.margin, options.margin, 0, 0)
editor.renderer.setPadding(options.margin)
/*
* Toolbar
*/
this.$toolbar.find('>ul>li>a')
.each(function(){
var abbr = $(this).find('>abbr'),
label = abbr.text(),
help = abbr.attr('title'),
title = label + ' (<strong>' + help + '</strong>)';
$(this).attr('title', title)
})
.tooltip({
delay: 500,
placement: 'top',
html: true
})
;
this.$fullscreenDisable.hide()
this.$fullscreenEnable.on('click.codeeditor', '>a', $.proxy(this.toggleFullscreen, this))
this.$fullscreenDisable.on('click.codeeditor', '>a', $.proxy(this.toggleFullscreen, this))
this.$searchboxDisable.hide()
this.$searchboxEnable.on('click.codeeditor', '>a', $.proxy(this.toggleSearchbox, this))
this.$searchboxDisable.on('click.codeeditor', '>a', $.proxy(this.toggleSearchbox, this))
this.$replaceboxDisable.hide()
this.$replaceboxEnable.on('click.codeeditor', '>a', $.proxy(this.toggleReplacebox, this))
this.$replaceboxDisable.on('click.codeeditor', '>a', $.proxy(this.toggleReplacebox, this))
/*
* Hotkeys
*/
this.$el.hotKey({
hotkey: 'esc',
callback: this.proxy(this.onEscape)
})
editor.commands.addCommand({
name: 'toggleFullscreen',
bindKey: { win: 'Ctrl+Shift+F', mac: 'Ctrl+Shift+F' },
exec: $.proxy(this.toggleFullscreen, this),
readOnly: true
})
}
CodeEditor.prototype.dispose = function() {
if (this.$el === null)
return
this.unregisterHandlers()
this.disposeAttachedControls()
this.$el = null
this.$textarea = null
this.$toolbar = null
this.$code = null
this.$fullscreenEnable = null
this.$fullscreenDisable = null
this.$searchboxEnable = null
this.$searchboxDisable = null
this.$replaceboxEnable = null
this.$replaceboxDisable = null
this.$form = null
this.options = null
BaseProto.dispose.call(this)
}
CodeEditor.prototype.disposeAttachedControls = function() {
this.editor.destroy()
var keys = Object.keys(this.editor.renderer)
for (var i=0, len=keys.length; i<len; i++)
this.editor.renderer[keys[i]] = null
keys = Object.keys(this.editor)
for (var i=0, len=keys.length; i<len; i++)
this.editor[keys[i]] = null
this.editor = null
this.$toolbar.find('>ul>li>a').tooltip('destroy')
this.$el.removeData('oc.codeEditor')
this.$el.hotKey('dispose')
}
CodeEditor.prototype.unregisterHandlers = function() {
this.editor.off('change', this.proxy(this.onChange))
this.editor.off('blur', this.proxy(this.onBlur))
this.editor.off('focus', this.proxy(this.onFocus))
this.$fullscreenEnable.off('.codeeditor')
this.$fullscreenDisable.off('.codeeditor')
this.$form.off('oc.beforeRequest', this.proxy(this.onBeforeRequest))
this.$el.off('dispose-control', this.proxy(this.dispose))
$(window).off('resize', this.proxy(this.onResize))
$(window).off('oc.updateUi', this.proxy(this.onResize))
}
CodeEditor.prototype.onBeforeRequest = function() {
this.$textarea.val(this.editor.getSession().getValue())
}
CodeEditor.prototype.onChange = function() {
this.$textarea.trigger('change')
this.$textarea.trigger('oc.codeEditorChange')
}
CodeEditor.prototype.onResize = function() {
this.editor.resize()
}
CodeEditor.prototype.onBlur = function() {
this.$el.removeClass('editor-focus')
}
CodeEditor.prototype.onFocus = function() {
this.$el.addClass('editor-focus')
}
CodeEditor.prototype.onEscape = function() {
this.isFullscreen && this.toggleFullscreen()
}
CodeEditor.prototype.setWordWrap = function(mode) {
var session = this.editor.getSession(),
renderer = this.editor.renderer
switch (mode + '') {
default:
case "off":
session.setUseWrapMode(false)
renderer.setPrintMarginColumn(80)
break
case "40":
session.setUseWrapMode(true)
session.setWrapLimitRange(40, 40)
renderer.setPrintMarginColumn(40)
break
case "80":
session.setUseWrapMode(true)
session.setWrapLimitRange(80, 80)
renderer.setPrintMarginColumn(80)
break
case "fluid":
session.setUseWrapMode(true)
session.setWrapLimitRange(null, null)
renderer.setPrintMarginColumn(80)
break
}
}
CodeEditor.prototype.setTheme = function(theme) {
var self = this
assetManager.load({
js:[
this.options.vendorPath + '/theme-' + theme + '.js'
]
}, function(){
self.editor.setTheme('ace/theme/' + theme)
})
}
CodeEditor.prototype.getContent = function() {
return this.editor.getSession().getValue()
}
CodeEditor.prototype.setContent = function(html) {
this.editor.getSession().setValue(html)
}
CodeEditor.prototype.getEditorObject = function() {
return this.editor
}
CodeEditor.prototype.getToolbar = function() {
return this.$toolbar
}
CodeEditor.prototype.toggleFullscreen = function() {
this.$el.toggleClass('editor-fullscreen')
this.$fullscreenEnable.toggle()
this.$fullscreenDisable.toggle()
this.isFullscreen = this.$el.hasClass('editor-fullscreen')
if (this.isFullscreen) {
$('body').css({ overflow: 'hidden' })
}
else {
$('body').css({ overflow: 'inherit' })
}
this.editor.resize()
this.editor.focus()
}
CodeEditor.prototype.toggleSearchbox = function() {
this.$searchboxEnable.toggle()
this.$searchboxDisable.toggle()
this.editor.execCommand("find")
this.editor.resize()
this.editor.focus()
}
CodeEditor.prototype.toggleReplacebox = function() {
this.$replaceboxEnable.toggle()
this.$replaceboxDisable.toggle()
this.editor.execCommand("replace")
this.editor.resize()
this.editor.focus()
}
// CODEEDITOR PLUGIN DEFINITION
// ============================
var old = $.fn.codeEditor
$.fn.codeEditor = function (option) {
var args = Array.prototype.slice.call(arguments, 1), result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.codeEditor')
var options = $.extend({}, CodeEditor.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.codeEditor', (data = new CodeEditor(this, options)))
if (typeof option == 'string') result = data[option].apply(data, args)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.codeEditor.Constructor = CodeEditor
if ($.wn === undefined)
$.wn = {}
if ($.oc === undefined)
$.oc = $.wn
$.wn.codeEditorExtensionModes = {
'htm': 'html',
'html': 'html',
'md': 'markdown',
'txt': 'plain_text',
'js': 'javascript',
'less': 'less',
'scss': 'scss',
'sass': 'sass',
'css': 'css'
}
// CODEEDITOR NO CONFLICT
// =================
$.fn.codeEditor.noConflict = function () {
$.fn.codeEditor = old
return this
}
// CODEEDITOR DATA-API
// ===============
$(document).render(function () {
$('[data-control="ace-codeeditor"]').codeEditor()
});
// FIX EMMET HTML WHEN SYNTAX IS TWIG
// ==================================
+function (exports) {
if (exports.ace && typeof exports.ace.require == 'function') {
var emmetExt = exports.ace.require('ace/ext/emmet')
if (emmetExt && emmetExt.AceEmmetEditor && emmetExt.AceEmmetEditor.prototype.getSyntax) {
var coreGetSyntax = emmetExt.AceEmmetEditor.prototype.getSyntax
emmetExt.AceEmmetEditor.prototype.getSyntax = function () {
var $syntax = $.proxy(coreGetSyntax, this)()
return $syntax == 'twig' ? 'html' : $syntax
};
}
}
}(window)
}(window.jQuery);