Commit 147c3ce4 authored by Jan Köster's avatar Jan Köster
Browse files

test

parent 6611f325
Loading
Loading
Loading
Loading

editor/node/node_widget_runner.js

deleted100644 → 0
+0 −103
Changes for editor/node/node_widget_runner.js: 0 added lines, 103 removed lines.
Original line number Diff line number Diff line
'use strict';

const fs = require('fs');
const path = require('path');

async function main() {
  const [, , requestFile, responseFile] = process.argv;
  if (!requestFile || !responseFile) {
    throw new Error('Usage: node node_widget_runner.js <request.json> <response.json>');
  }

  const reqRaw = fs.readFileSync(requestFile, 'utf8');
  const req = JSON.parse(reqRaw);
  const hook = typeof req.hook === 'string' && req.hook ? req.hook : 'render';

  if (!req.script_path) {
    throw new Error('request.script_path is required');
  }

  const scriptPath = path.resolve(req.script_path);
  const mod = require(scriptPath);
  const fn = resolveHook(mod, hook);

  if (!fn) {
    fs.writeFileSync(
      responseFile,
      JSON.stringify({ status: 'success', missing_hook: true, result: null }),
      'utf8'
    );
    return;
  }

  const result = await Promise.resolve(fn(req));

  if (hook === 'metadata' && result && typeof result === 'object') {
    if (!result.type_id || typeof result.type_id !== 'string' || !result.type_id.trim()) {
      const scriptBase = path.basename(scriptPath, path.extname(scriptPath));
      const widgetName = (typeof result.name === 'string' && result.name.trim()) ? result.name.trim() : scriptBase;
      if (typeof mod.getUUID === 'function') {
        result.type_id = String(mod.getUUID());
      }
      if (!result.type_id || !result.type_id.trim()) {
        throw new Error(`Widget ${widgetName} has no hardcoded UUID (type_id/getUUID missing)`);
      }
    }
  }

  if (hook === 'render') {
    let html = '';
    if (typeof result === 'string') {
      html = result;
    } else if (result && typeof result === 'object' && typeof result.html === 'string') {
      html = result.html;
    } else {
      throw new Error('render() must return a string or { html: string }');
    }

    fs.writeFileSync(
      responseFile,
      JSON.stringify({ status: 'success', html, result: { html } }),
      'utf8'
    );
    return;
  }

  fs.writeFileSync(
    responseFile,
    JSON.stringify({ status: 'success', missing_hook: false, result }),
    'utf8'
  );
}

function resolveHook(mod, hook) {
  if (hook === 'render') {
    if (typeof mod === 'function') return mod;
    if (typeof mod.render === 'function') return mod.render;
    return null;
  }

  if (typeof mod[hook] === 'function') return mod[hook];
  if (mod.hooks && typeof mod.hooks[hook] === 'function') return mod.hooks[hook];
  return null;
}

main().catch((err) => {
  const [, , , responseFile] = process.argv;
  const payload = JSON.stringify({
    status: 'error',
    message: err && err.message ? err.message : 'Unknown Node runner error'
  });

  if (responseFile) {
    try {
      fs.writeFileSync(responseFile, payload, 'utf8');
    } catch (_) {
      process.stderr.write(payload + '\n');
    }
  } else {
    process.stderr.write(payload + '\n');
  }

  process.exitCode = 1;
});

editor/node/sample_widget.js

deleted100644 → 0
+0 −61
Changes for editor/node/sample_widget.js: 0 added lines, 61 removed lines.
Original line number Diff line number Diff line
'use strict';

exports.metadata = function metadata() {
  return {
    name: 'Node Sample Widget',
    author: 'blogi',
    version: '1.0',
    can_have_children: false
  };
};

exports.settings = function settings(ctx) {
  const p = (ctx && ctx.properties) || {};
  const title = typeof p.title === 'string' ? p.title : '';
  const text = typeof p.text === 'string' ? p.text : '';
  return {
    html: `
<label>Title: </label><input type="text" name="title" value="${escapeHtml(title)}" />
<label>Text: </label><textarea name="text" rows="5" cols="80">${escapeHtml(text)}</textarea>`
  };
};

exports.json_api = function jsonApi(ctx) {
  const req = (ctx && ctx.payload && ctx.payload.request) || {};
  if (req.action === 'ping') {
    return { pong: true, message: 'hello from node widget' };
  }
  return {};
};

exports.render = function render(ctx) {
  const p = (ctx && ctx.properties) || {};
  const title = typeof p.title === 'string' ? p.title : 'Node Widget';
  const text = typeof p.text === 'string' ? p.text : 'Rendered by embedded Node.js';
  const color = typeof p.color === 'string' ? p.color : '#0f766e';

  return `
<section style="border:1px solid ${color};padding:12px;border-radius:8px;">
  <h3 style="margin:0 0 8px 0;color:${color};">${escapeHtml(title)}</h3>
  <p style="margin:0;">${escapeHtml(text)}</p>
</section>`;
};

exports.have_childs = function haveChilds(ctx) {
  const current = !!(ctx && ctx.payload && ctx.payload.have_childs);
  return { have_childs: current };
};

exports.set_child = function setChild() { return {}; };
exports.unset_child = function unsetChild() { return {}; };
exports.add_next = function addNext() { return {}; };
exports.remove_next = function removeNext() { return {}; };

function escapeHtml(input) {
  return String(input)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}
+0 −0

Empty file deleted.

+0 −76
Changes for editor/node/widgets/_base_widget.js: 0 added lines, 76 removed lines.
Original line number Diff line number Diff line
'use strict';

exports.makeWidget = function makeWidget(meta) {
  const defaults = {
    name: 'NodeWidget',
    author: 'Jan Koester',
    version: '1.0',
    canHaveChildren: false,
    className: 'we-node-widget'
  };
  const cfg = Object.assign({}, defaults, meta || {});

  return {
    metadata() {
      return {
        name: cfg.name,
        author: cfg.author,
        version: cfg.version,
        can_have_children: !!cfg.canHaveChildren
      };
    },

    settings(ctx) {
      const p = (ctx && ctx.properties) || {};
      const title = toStr(p.title);
      const text = toStr(p.text);
      const cssClass = toStr(p.css_class || cfg.className);
      return {
        html: `
<label>Title: </label><input type="text" name="title" value="${escapeHtml(title)}" />
<label>Text: </label><textarea name="text" rows="5" cols="80">${escapeHtml(text)}</textarea>
<label>CSS Class: </label><input type="text" name="css_class" value="${escapeHtml(cssClass)}" />`
      };
    },

    json_api(ctx) {
      const req = (ctx && ctx.payload && ctx.payload.request) || {};
      if (req.action === 'ping') {
        return { pong: true, widget: cfg.name };
      }
      return {};
    },

    render(ctx) {
      const p = (ctx && ctx.properties) || {};
      const title = toStr(p.title || cfg.name);
      const text = toStr(p.text || 'Node.js widget render');
      const cssClass = toStr(p.css_class || cfg.className);
      return {
        html: `<section class="${escapeHtml(cssClass)}"><h3>${escapeHtml(title)}</h3><p>${escapeHtml(text)}</p></section>`
      };
    },

    have_childs(ctx) {
      return { have_childs: !!(ctx && ctx.payload && ctx.payload.have_childs) };
    },

    set_child() { return {}; },
    unset_child() { return {}; },
    add_next() { return {}; },
    remove_next() { return {}; }
  };
};

function toStr(v) {
  return typeof v === 'string' ? v : '';
}

function escapeHtml(input) {
  return String(input)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

editor/node/widgets/accordion.js

deleted100644 → 0
+0 −79
Changes for editor/node/widgets/accordion.js: 0 added lines, 79 removed lines.
Original line number Diff line number Diff line
'use strict';

module.exports = {
  metadata() {
    return { name: 'Accordion', author: 'Jan Koester', version: '1.0', can_have_children: true };
  },

  json_api(ctx) {
    const req = (ctx && ctx.payload && ctx.payload.request) || {};
    if (req.action !== 'get_schema') return {};
    return {
      schema: [
        field('title', 'Title', 'text', 'Accordion'),
        field('allow_multiple', 'Allow Multiple Open', 'checkbox'),
        field('border_style', 'Border', 'text', '1px solid var(--blogi-border, #ccc)'),
        field('header_bg', 'Header Background', 'color_var'),
        field('content_bg', 'Content Background', 'color_var'),
        field('hidden', 'Hidden', 'checkbox', null, 'desktop', null, false),
        field('custom_css', 'Custom CSS', 'textarea', '.we-xxx { ... }')
      ]
    };
  },

  render(ctx) {
    const p = (ctx && ctx.properties) || {};
    const id = safe((ctx && ctx.instance_id) || 'accordion');
    const cls = `we-accordion-${id}`;
    const title = esc(str(p.title) || 'Accordion');
    const allowMultiple = bool(p.allow_multiple, false);

    return {
      html: `<style>
.${cls}{${bool(p.hidden,false)?'display:none;':''}}
.${cls}-item{${css('border', str(p.border_style) || '1px solid var(--blogi-border, #ccc)')}}
.${cls}-head{cursor:pointer;padding:10px 12px;${css('background', str(p.header_bg))}}
.${cls}-body{display:none;padding:10px 12px;${css('background', str(p.content_bg))}}
${str(p.custom_css)}
</style>
<div class="${cls}" data-acc-root="1">
  <div class="${cls}-item">
    <div class="${cls}-head" data-acc-head="1">${title}</div>
    <div class="${cls}-body" data-acc-body="1"></div>
  </div>
</div>
<script>(function(){
 var s=document.currentScript,h=s&&s.closest('.we-node-widget');if(!h)return;
 var root=h.querySelector('[data-acc-root="1"]');var body=h.querySelector('[data-acc-body="1"]');
 var kids=Array.from(h.children).filter(function(el){return el.classList&&el.classList.contains('we-node-widget');});
 kids.forEach(function(k){if(body)body.appendChild(k);});
 var heads=root?root.querySelectorAll('[data-acc-head="1"]'):[];
 heads.forEach(function(head){head.addEventListener('click',function(){
   var item=head.parentElement;var b=item.querySelector('[data-acc-body="1"]');if(!b)return;
   var open=b.style.display==='block';
   if(!${allowMultiple ? 'true' : 'false'}){heads.forEach(function(hh){var bb=hh.parentElement.querySelector('[data-acc-body="1"]');if(bb)bb.style.display='none';});}
   b.style.display=open?'none':'block';
 });});
})();</script>`
    };
  },

  have_childs(ctx) { return { have_childs: !!(ctx && ctx.payload && ctx.payload.have_childs) }; },
  set_child() { return {}; },
  unset_child() { return {}; },
  add_next() { return {}; },
  remove_next() { return {}; }
};

function field(key,label,type,placeholder,target,options,defVal){const f={key,label,type,target:target||'all'};if(placeholder)f.placeholder=placeholder;if(Array.isArray(options))f.options=options;if(typeof defVal!=='undefined')f.default=defVal;return f;}
function str(v){return typeof v==='string'?v:'';}
function bool(v,d){if(typeof v==='boolean')return v;if(typeof v==='number')return v!==0;if(typeof v==='string')return v==='true'||v==='1';return !!d;}
function css(k,v){const s=str(v);return s?`${k}:${s};`:'';}
function safe(v){return String(v).replace(/[^a-zA-Z0-9_-]/g,'_');}
function esc(v){return String(v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}

function getUUID() {
  return 'a2f7c1e3-49d8-4b5a-9c6e-8d1f3a7b2e04';
}

module.exports.getUUID = getUUID;
Loading