/** * Vvveb * * Copyright (C) 2021 Ziadin Givan * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * https://github.com/givanz/Vvveb */ Vvveb.ComponentsGroup['Widgets'] = ["widgets/googlemaps", "widgets/embed-video", "widgets/chartjs", "widgets/lottie",/* "widgets/facebookpage", */"widgets/paypal", /*"widgets/instagram",*/ "widgets/twitter", "widgets/openstreetmap"/*, "widgets/facebookcomments"*/]; Vvveb.Components.extend("_base", "widgets/googlemaps", { name: "Google Maps", attributes: ["data-component-maps"], image: "icons/map.svg", dragHtml: '', html: '
', resizable:true,//show select box resize handlers resizeMode:"css", //url parameters z:3, //zoom q:'Paris',//location t: 'q', //map type q = roadmap, w = satellite key: '', init: function (node) { let iframe = node.querySelector('iframe'); let url = new URL(iframe.getAttribute("src")); let params = new URLSearchParams(url.search); this.z = params.get("z"); this.q = params.get("q"); this.t = params.get("t"); this.key = params.get("key"); document.querySelector(".component-properties input[name=z]").value = this.z; document.querySelector(".component-properties input[name=q]").value = this.q; document.querySelector(".component-properties select[name=t]").value = this.t; document.querySelector(".component-properties input[name=key]").value = this.key; }, onChange: function (node, property, value) { map_iframe = node.querySelector('iframe'); this[property.key] = value; mapurl = 'https://maps.google.com/maps?q=' + this.q + '&z=' + this.z + '&t=' + this.t + '&output=embed'; map_iframe.setAttribute("src",mapurl); return node; }, properties: [{ name: "Address", key: "q", inputtype: TextInput },{ name: "Map type", key: "t", inputtype: SelectInput, data:{ options: [{ value: "q", text: "Roadmap" },{ value: "w", text: "Satellite" }] }, },{ name: "Zoom", key: "z", inputtype: RangeInput, data:{ max: 20, //max zoom level min:1, step:1 } },{ name: "Key", key: "key", inputtype: TextInput }] }); Vvveb.Components.extend("_base", "widgets/openstreetmap", { name: "Open Street Map", attributes: ["data-component-openstreetmap"], image: "icons/map.svg", dragHtml: '', html: `
`, resizable:true,//show select box resize handlers resizeMode:"css", //url parameters bbox:'',//location layer: 'mapnik', //map type init: function (node) { let iframe = node.querySelector('iframe'); let url = new URL(iframe.getAttribute("src")); let params = new URLSearchParams(url.search); this.bbox = params.get("bbox"); this.layer = params.get("layer"); document.querySelector(".component-properties input[name=bbox]").value = this.bbox; document.querySelector(".component-properties input[name=layer]").value = this.layer; }, onChange: function (node, property, value) { map_iframe = node.querySelector('iframe'); this[property.key] = value; mapurl = 'https://www.openstreetmap.org/export/embed.html?bbox=' + this.bbox + '&layer=' + this.layer; map_iframe.setAttribute("src",mapurl); return node; }, properties: [{ name: "Map", key: "bbox", inputtype: TextInput /* },{ name: "Layer", key: "layer", inputtype: SelectInput, data:{ options: [{ value: "", text: "Default" },{ value: "Y", text: "CyclOSM" },{ value: "C", text: "Cycle Map" },{ value: "T", text: "Transport Map" }] }*/ }] }); Vvveb.Components.extend("_base", "widgets/embed-video", { name: "Embed Video", attributes: ["data-component-video"], image: "icons/youtube.svg", dragHtml: '', //use image for drag and swap with iframe on drop for drag performance html: '
', //url parameters set with onChange t:'y',//video type video_id:'',//video id url: '', //html5 video src autoplay: false, controls: false, loop: false, playsinline: true, muted: false, resizable:true,//show select box resize handlers resizeMode:"css",//div unlike img/iframe etc does not have width,height attributes need to use css youtubeRegex:/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]+)/i, vimeoRegex : /(?:vimeo\.com(?:[^\d]+))(\d+)/i, init: function (node) { iframe = node.querySelector('iframe'); video = node.querySelector('video'); document.querySelector(".component-properties [data-key=url]").style.display = "none"; document.querySelector(".component-properties [data-key=poster]").style.display = "none"; //check if html5 if (video) { this.url = video.src; } else if (iframe) {//vimeo or youtube let src = iframe.getAttribute("src"); let match; if (src && src.indexOf("youtube") && (match = src.match(this.youtubeRegex))) {//youtube this.video_id = match[1]; this.t = "y"; } else if (src && src.indexOf("vimeo") && (match = src.match(this.vimeoRegex))) { //vimeo this.video_id = match[1]; this.t = "v"; } else { this.t = "h"; } } document.querySelector(".component-properties input[name=video_id]").value = this.video_id; document.querySelector(".component-properties input[name=url]").value = this.url; document.querySelector(".component-properties select[name=t]").value = this.t; }, onChange: function (node, property, value) { this[property.key] = value; //if (property.key == "t") { switch (this.t) { case 'y': document.querySelector(".component-properties [data-key=video_id]").style.display = ""; document.querySelector(".component-properties [data-key=url]").style.display = "none"; document.querySelector(".component-properties [data-key=poster]").style.display = "none"; newnode = generateElements(``)[0]; break; case 'v': document.querySelector(".component-properties [data-key=video_id]").style.display = ""; document.querySelector(".component-properties [data-key=url]").style.display = "none"; document.querySelector(".component-properties [data-key=poster]").style.display = "none"; newnode = generateElements(``)[0]; break; case 'h': document.querySelector(".component-properties [data-key=video_id]").style.display = "none"; document.querySelector(".component-properties [data-key=url]").style.display = ""; document.querySelector(".component-properties [data-key=poster]").style.display = ""; newnode = generateElements('')[0]; break; } node.querySelector(":scope > iframe,:scope > video").replaceWith(newnode); return node; } return node; }, properties: [{ name: "Provider", key: "t", inputtype: SelectInput, data:{ options: [{ text: "Youtube", value: "y" },{ text: "Vimeo", value: "v" },{ text: "HTML5", value: "h" }] }, },{ name: "Video", key: "video_id", inputtype: TextInput, onChange: function(node, value, input, component) { let youtube = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]+)/i; let vimeo = /(?:vimeo\.com(?:[^\d]+))(\d+)/i; let id = false; let t = false; if (((id = value.match(youtube)) && (t = "y")) || ((id = value.match(vimeo)) && (t = "v"))) { document.querySelector(".component-properties select[name=t]").value = t; document.querySelector(".component-properties select[name=video_id]").value = id[1]; component.t = t; component.video_id = id[1]; return id[1]; } return node; } },{ name: "Poster", key: "poster", htmlAttr: "poster", inputtype: ImageInput },{ name: "Url", key: "url", inputtype: TextInput },{ name: "Width", key: "width", htmlAttr: "style", inline:false, col:6, inputtype: CssUnitInput },{ name: "Height", key: "height", htmlAttr: "style", inline:false, col:6, inputtype: CssUnitInput },{ key: "video_options", inputtype: SectionInput, name:false, data: {header:"Options"}, },{ name: "Auto play", key: "autoplay", htmlAttr: "autoplay", inline:true, col:4, inputtype: CheckboxInput },{ name: "Plays inline", key: "playsinline", htmlAttr: "playsinline", inline:true, col:4, inputtype: CheckboxInput },{ name: "Controls", key: "controls", htmlAttr: "controls", inline:true, col:4, inputtype: CheckboxInput },{ name: "Loop", key: "loop", htmlAttr: "loop", inline:true, col:4, inputtype: CheckboxInput },{ name: "Muted", key: "muted", htmlAttr: "muted", inline:true, col:4, inputtype: CheckboxInput },{ name:"", key: "autoplay_warning", inline:false, col:12, inputtype: NoticeInput, data: { type:'warning', title:'Autoplay', text:'Most browsers allow auto play only if video is muted and plays inline' } }] }); Vvveb.Components.extend("_base", "widgets/facebookcomments", { name: "Facebook Comments", attributes: ["data-component-facebookcomments"], image: "icons/facebook.svg", dragHtml: '', html: '
\
', properties: [{ name: "Href", key: "business", htmlAttr: "data-href", child:".fb-comments", inputtype: TextInput },{ name: "Item name", key: "item_name", htmlAttr: "data-numposts", child:".fb-comments", inputtype: TextInput },{ name: "Color scheme", key: "colorscheme", htmlAttr: "data-colorscheme", child:".fb-comments", inputtype: TextInput },{ name: "Order by", key: "order-by", htmlAttr: "data-order-by", child:".fb-comments", inputtype: TextInput },{ name: "Currency code", key: "width", htmlAttr: "data-width", child:".fb-comments", inputtype: TextInput }] }); /* Vvveb.Components.extend("_base", "widgets/instagram", { name: "Instagram", attributes: ["data-component-instagram"], image: "icons/instagram.svg", drophtml: '', html: '
\

Text

A post shared by Instagram (@instagram) on

\ \
', properties: [{ name: "Widget id", key: "instgrm-permalink", htmlAttr: "data-instgrm-permalink", child: ".instagram-media", inputtype: TextInput }], }); */ Vvveb.Components.extend("_base", "widgets/twitter", { name: "Twitter", attributes: ["data-component-twitter"], image: "icons/twitter.svg", dragHtml: '', html: '
', resizable:true,//show select box resize handlers resizeMode:"css", twitterRegex : /(?:twitter\.com(?:[^\d]+))(\d+)/i, tweet:'',//location init: function (node) { let iframe = node.querySelector('iframe'); let src = iframe.getAttribute("src"); let url = new URL(src); let params = new URLSearchParams(url.search); this.tweet = params.get("id"); if (!this.tweet) { if (match = src.match(this.twitterRegex)) { this.tweet = match[1]; } } document.querySelector(".component-properties input[name=tweet]").value = this.tweet; }, onChange: function (node, property, value) { tweet_iframe = node.querySelector('iframe'); if (property.key == "tweet") { this[property.key] = value; tweeturl = 'https://platform.twitter.com/embed/Tweet.html?embedId=twitter-widget-0&frame=false&hideCard=false&hideThread=false&id=' + this.tweet; tweet_iframe.setAttribute("src",tweeturl); } return node; }, properties: [{ name: "Tweet", key: "tweet", inputtype: TextInput, onChange: function(node, value, input, component) { let twitterRegex = /(?:twitter\.com(?:[^\d]+))(\d+)/i; let id = false; if (id = value.match(twitterRegex)) { document.querySelector(".component-properties input[name=tweet]").value = id[1]; component.tweet = id[1]; return id[1]; } return node; } }] }); Vvveb.Components.extend("_base", "widgets/paypal", { name: "Paypal", attributes: ["data-component-paypal"], image: "icons/paypal.svg", html: '
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
', properties: [{ name: "Email", key: "business", htmlAttr: "value", child:"input[name='business']", inputtype: TextInput },{ name: "Item name", key: "item_name", htmlAttr: "value", child:"input[name='item_name']", inputtype: TextInput },{ name: "Item number", key: "item_number", htmlAttr: "value", child:"input[name='item_number']", inputtype: TextInput },{ name: "Currency code", key: "currency_code", htmlAttr: "value", child:"input[name='currency_code']", inputtype: TextInput }], }); Vvveb.Components.extend("_base", "widgets/facebookpage", { name: "Facebook Page Plugin", attributes: ["data-component-facebookpage"], image: "icons/facebook.svg", dropHtml: '', html: `
`, properties: [{ name: "Small header", key: "small-header", htmlAttr: "data-small-header", child:".fb-page", inputtype: TextInput },{ name: "Adapt container width", key: "adapt-container-width", htmlAttr: "data-adapt-container-width", child:".fb-page", inputtype: TextInput },{ name: "Hide cover", key: "hide-cover", htmlAttr: "data-hide-cover", child:".fb-page", inputtype: TextInput },{ name: "Show facepile", key: "show-facepile", htmlAttr: "data-show-facepile", child:".fb-page", inputtype: TextInput },{ name: "App Id", key: "appid", htmlAttr: "data-appId", child:".fb-page", inputtype: TextInput }], onChange: function(node, input, value, component) { let newElement = generateElements(this.html)[0]; newElement.find(".fb-page").setAttribute(input.htmlAttr, value); frameHead.querySelector("[data-fbcssmodules]").remove(); frameBody.querySelector("[data-fbcssmodules]").remove(); frameHead.querySelector("script[src^='https://connect.facebook.net']").remove(); node.parent().html(newElement.html()); return newElement; } }); Vvveb.Components.extend("_base", "widgets/chartjs", { name: "Chart.js", attributes: ["data-component-chartjs"], image: "icons/chart.svg", dragHtml: '', html: '
\ \
', chartjs: null, ctx: null, node: null, config: {/* type: 'line', data: { labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], datasets: [{ data: [12, 19, 3, 5, 2, 3], fill: false, borderColor:'rgba(255, 99, 132, 0.2)', },{ fill: false, data: [3, 15, 7, 4, 19, 12], borderColor: 'rgba(54, 162, 235, 0.2)', }] },*/ }, dragStart: function (node) { //check if chartjs is included and if not add it when drag starts to allow the script to load body = Vvveb.Builder.frameBody; if (document.getElementById("#chartjs-script")) { body.append(generateElements('')[0]); body.append(generateElements('')[0]); } return node; }, drawChart: function () { if (this.chartjs != null) this.chartjs.destroy(); this.node.dataset.chart = JSON.stringify(this.config); config = Object.assign({}, this.config);//avoid passing by reference to avoid chartjs to fill the object this.chartjs = new Chart(this.ctx, config); }, init: function (node) { this.node = node; this.ctx = node.querySelector("canvas").getContext("2d"); this.config = JSON.parse(node.dataset.chart); this.drawChart(); return node; }, beforeInit: function (node) { return node; }, properties: [{ name: "Type", key: "type", inputtype: SelectInput, data:{ options: [{ text: "Line", value: "line" },{ text: "Bar", value: "bar" },{ text: "Pie", value: "pie" },{ text: "Doughnut", value: "doughnut" },{ text: "Polar Area", value: "polarArea" },{ text: "Bubble", value: "bubble" },{ text: "Scatter", value: "scatter" },{ text: "Radar", value: "radar" }] }, init: function(node) { return JSON.parse(node.dataset.chart).type; }, onChange: function(node, value, input, component) { component.config.type = value; component.drawChart(); return node; } }] }); function lottieAfterDrop(node) { //check if lottie js is included and if not add it when drag starts to allow the script to load body = Vvveb.Builder.frameBody; if (!body.querySelector("#lottie-js")) { let lib = document.createElement('script'); let code = document.createElement('script'); lib.id = 'lottie-js'; lib.type = 'text/javascript'; lib.src = 'https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js'; code.type = 'text/javascript'; code.text = ` let lottie = []; function initLottie(onlyNew = false) { if (typeof bodymovin == "undefined") return; let list = document.querySelectorAll('.lottie' + (onlyNew ? ":not(.lottie-initialized)" : "") ); list.forEach(el => { el.replaceChildren(); let animItem = bodymovin.loadAnimation({ wrapper: el, animType: 'svg', loop: (el.dataset.loop == "true" ? true : false), autoplay: (el.dataset.autoplay == "true" ? true : false), path: el.dataset.path }); }); } if (document.readyState !== 'loading') { initLottie(); } else { document.addEventListener('DOMContentLoaded', initLottie); }`; body.appendChild(lib); body.appendChild(code); lib.addEventListener('load', function() { Vvveb.Builder.iframe.contentWindow.initLottie(); }); } else { Vvveb.Builder.iframe.contentWindow.initLottie(true); } return node; }; Vvveb.Components.add("widgets/lottie", { name: "Lottie", image: "icons/lottie.svg", attributes: ["data-component-lottie"], html: `
`, afterDrop: lottieAfterDrop, onChange: function (node, property, value) { Vvveb.Builder.iframe.contentWindow.initLottie(); Vvveb.Builder.selectNode(node); return node; }, properties: [{ name: "Path", key: "path", //inputtype: ImageInput, inputtype: TextInput, htmlAttr:"data-path", },{ name: "Autoplay", key: "autoplay", htmlAttr:"data-autoplay", inputtype: CheckboxInput, inline:true, col:4 },{ name: "Loop", key: "loop", htmlAttr:"data-loop", inputtype: CheckboxInput, inline:true, col:4 }] });