@themarioga/grid-editor 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +53 -1
- package/dist/grideditor.css +148 -0
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/jquery.grideditor.bundle.min.js +2 -2
- package/dist/jquery.grideditor.js +555 -9
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/dist/locales/grideditor.es.js +32 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +1 -1
- package/dist/plugins/grideditor.alignment.js +81 -0
- package/dist/plugins/grideditor.alignment.min.js +2 -0
- package/dist/plugins/grideditor.alignment.min.js.map +1 -0
- package/dist/plugins/grideditor.gutters.js +145 -0
- package/dist/plugins/grideditor.gutters.min.js +2 -0
- package/dist/plugins/grideditor.gutters.min.js.map +1 -0
- package/dist/plugins/grideditor.order.js +206 -0
- package/dist/plugins/grideditor.order.min.js +2 -0
- package/dist/plugins/grideditor.order.min.js.map +1 -0
- package/dist/plugins/grideditor.spacing.js +299 -0
- package/dist/plugins/grideditor.spacing.min.js +2 -0
- package/dist/plugins/grideditor.spacing.min.js.map +1 -0
- package/dist/plugins/grideditor.visibility.js +172 -0
- package/dist/plugins/grideditor.visibility.min.js +2 -0
- package/dist/plugins/grideditor.visibility.min.js.map +1 -0
- package/docs/events.md +20 -1
- package/docs/locale-keys.md +52 -0
- package/docs/plugins.md +108 -3
- package/example/autosave.html +5 -0
- package/example/basic.html +5 -0
- package/example/breakpoints.html +5 -0
- package/example/ckeditor.html +5 -0
- package/example/containers.html +4 -0
- package/example/elements.html +5 -0
- package/example/index.html +24 -1
- package/example/locale.html +5 -0
- package/example/plugins.html +5 -0
- package/example/summernote.html +5 -0
- package/example/utilities.html +145 -0
- package/example/wrap_content.html +5 -0
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +555 -9
- package/src/js/locales/grideditor.es.js +32 -0
- package/src/js/plugins/grideditor.alignment.js +81 -0
- package/src/js/plugins/grideditor.gutters.js +145 -0
- package/src/js/plugins/grideditor.order.js +206 -0
- package/src/js/plugins/grideditor.spacing.js +299 -0
- package/src/js/plugins/grideditor.visibility.js +172 -0
- package/src/less/grideditor.less +167 -0
|
@@ -32,6 +32,8 @@ var METHODS = {
|
|
|
32
32
|
addTab: { value: true },
|
|
33
33
|
addAccordionItem: { value: true },
|
|
34
34
|
setLocale: {},
|
|
35
|
+
getUtility: { value: true },
|
|
36
|
+
setUtility: { value: true },
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
/**
|
|
@@ -45,14 +47,17 @@ var PLACEMENTS = ['appendTo', 'prependTo', 'insertAfter', 'insertBefore'];
|
|
|
45
47
|
* runs in: a size written for a tier applies to every wider tier that does not
|
|
46
48
|
* override it. Every size and offset class grid-editor reads or writes comes
|
|
47
49
|
* from this table, so adding a tier is a row here and nothing else.
|
|
50
|
+
*
|
|
51
|
+
* `infix` is what Bootstrap's utility classes put between the property and
|
|
52
|
+
* the value - `order-md-2`, `d-none` - and is empty for the smallest tier.
|
|
48
53
|
*/
|
|
49
54
|
var BREAKPOINTS = [
|
|
50
|
-
{ key: 'xs', colPrefix: 'col-', offsetPrefix: 'offset-', min: 0, preview: 400, labelKey: 'view.xs' },
|
|
51
|
-
{ key: 'sm', colPrefix: 'col-sm-', offsetPrefix: 'offset-sm-', min: 576, preview: 576, labelKey: 'view.sm' },
|
|
52
|
-
{ key: 'md', colPrefix: 'col-md-', offsetPrefix: 'offset-md-', min: 768, preview: 768, labelKey: 'view.md' },
|
|
53
|
-
{ key: 'lg', colPrefix: 'col-lg-', offsetPrefix: 'offset-lg-', min: 992, preview: 992, labelKey: 'view.lg' },
|
|
54
|
-
{ key: 'xl', colPrefix: 'col-xl-', offsetPrefix: 'offset-xl-', min: 1200, preview: 1200, labelKey: 'view.xl' },
|
|
55
|
-
{ key: 'xxl', colPrefix: 'col-xxl-', offsetPrefix: 'offset-xxl-', min: 1400, preview: null, labelKey: 'view.xxl' },
|
|
55
|
+
{ key: 'xs', infix: '', colPrefix: 'col-', offsetPrefix: 'offset-', min: 0, preview: 400, labelKey: 'view.xs' },
|
|
56
|
+
{ key: 'sm', infix: 'sm', colPrefix: 'col-sm-', offsetPrefix: 'offset-sm-', min: 576, preview: 576, labelKey: 'view.sm' },
|
|
57
|
+
{ key: 'md', infix: 'md', colPrefix: 'col-md-', offsetPrefix: 'offset-md-', min: 768, preview: 768, labelKey: 'view.md' },
|
|
58
|
+
{ key: 'lg', infix: 'lg', colPrefix: 'col-lg-', offsetPrefix: 'offset-lg-', min: 992, preview: 992, labelKey: 'view.lg' },
|
|
59
|
+
{ key: 'xl', infix: 'xl', colPrefix: 'col-xl-', offsetPrefix: 'offset-xl-', min: 1200, preview: 1200, labelKey: 'view.xl' },
|
|
60
|
+
{ key: 'xxl', infix: 'xxl', colPrefix: 'col-xxl-', offsetPrefix: 'offset-xxl-', min: 1400, preview: null, labelKey: 'view.xxl' },
|
|
56
61
|
];
|
|
57
62
|
|
|
58
63
|
/**
|
|
@@ -258,7 +263,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
258
263
|
'container_tools' : [], // Host tools on container drawers
|
|
259
264
|
'tab_tools' : [], // Host tools on tab drawers
|
|
260
265
|
'accordion_tools' : [], // Host tools on accordion item drawers
|
|
261
|
-
'plugins' : null, //
|
|
266
|
+
'plugins' : null, // Plugins to use, of any kind; null means every one loaded
|
|
267
|
+
'utilities' : {}, // Options for the utility plugins, by plugin name
|
|
262
268
|
'elements' : NESTED_SETTINGS.elements, // Element level controls, below the column
|
|
263
269
|
'custom_filter' : '',
|
|
264
270
|
'content_types' : ['tinymce'],
|
|
@@ -748,6 +754,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
748
754
|
// when their editor is ready, and the drawers go back in.
|
|
749
755
|
canvas.on('ge-rte-ready', '.ge-content', function() {
|
|
750
756
|
plugins('onContentReady', $(this));
|
|
757
|
+
refreshPreviews($(this));
|
|
751
758
|
});
|
|
752
759
|
}
|
|
753
760
|
|
|
@@ -1136,6 +1143,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1136
1143
|
makeSortable();
|
|
1137
1144
|
makeResizable();
|
|
1138
1145
|
switchLayout(curView);
|
|
1146
|
+
refreshPreviews(canvas);
|
|
1139
1147
|
}
|
|
1140
1148
|
|
|
1141
1149
|
function deinit() {
|
|
@@ -1156,6 +1164,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1156
1164
|
hideDropMarker();
|
|
1157
1165
|
canvas.find('.ge-tools-drawer').remove();
|
|
1158
1166
|
plugins('onDeinit');
|
|
1167
|
+
// After the rich text editors have let go of their content areas:
|
|
1168
|
+
// one that rebuilt its area's DOM brought the preview styles back
|
|
1169
|
+
// with it, and the attribute recording them came back too
|
|
1170
|
+
clearPreviews(canvas);
|
|
1159
1171
|
unmarkContainers();
|
|
1160
1172
|
removeSortable();
|
|
1161
1173
|
removeResizable();
|
|
@@ -1236,6 +1248,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1236
1248
|
if (wanted(name)) { FEATURES[name] = factory(api); }
|
|
1237
1249
|
});
|
|
1238
1250
|
|
|
1251
|
+
$.each($.fn.gridEditor.utilities, function(name, factory) {
|
|
1252
|
+
if (!wanted(name)) { return; }
|
|
1253
|
+
|
|
1254
|
+
UTILITIES[name] = factory(api);
|
|
1255
|
+
(UTILITIES[name].families || []).forEach(function(family) {
|
|
1256
|
+
registerFamily(name, UTILITIES[name], family);
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1239
1260
|
$.each(FEATURES, function(name, feature) {
|
|
1240
1261
|
$.each(feature.methods || {}, function(method, implementation) {
|
|
1241
1262
|
featureMethods[method] = implementation;
|
|
@@ -1243,7 +1264,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1243
1264
|
});
|
|
1244
1265
|
|
|
1245
1266
|
(settings.plugins || []).forEach(function(name) {
|
|
1246
|
-
if (CONTAINERS[name] || FEATURES[name]) { return; }
|
|
1267
|
+
if (CONTAINERS[name] || FEATURES[name] || UTILITIES[name]) { return; }
|
|
1247
1268
|
|
|
1248
1269
|
warnOnceHere('plugin:' + name, 'the "' + name + '" plugin is not loaded: ' +
|
|
1249
1270
|
'include dist/plugins/grideditor.' + name + '.js after the editor');
|
|
@@ -1253,7 +1274,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1253
1274
|
/**
|
|
1254
1275
|
* A hook every loaded plugin may have. Containers first, since a
|
|
1255
1276
|
* feature that looks at the canvas - elements, say - wants the
|
|
1256
|
-
* containers already marked
|
|
1277
|
+
* containers already marked, and utilities last, since they decorate
|
|
1278
|
+
* nodes the other two may have just made.
|
|
1257
1279
|
*/
|
|
1258
1280
|
function plugins(hook, argument) {
|
|
1259
1281
|
$.each(CONTAINERS, function(type, definition) {
|
|
@@ -1262,6 +1284,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1262
1284
|
$.each(FEATURES, function(name, feature) {
|
|
1263
1285
|
if (feature[hook]) { feature[hook](argument); }
|
|
1264
1286
|
});
|
|
1287
|
+
$.each(UTILITIES, function(name, utility) {
|
|
1288
|
+
if (utility[hook]) { utility[hook](argument); }
|
|
1289
|
+
});
|
|
1265
1290
|
}
|
|
1266
1291
|
|
|
1267
1292
|
/** What a plugin is handed. See docs/plugins.md. */
|
|
@@ -1287,9 +1312,491 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1287
1312
|
emit: emit,
|
|
1288
1313
|
payloadFor: payloadFor,
|
|
1289
1314
|
operate: operate,
|
|
1315
|
+
kindOf: kindOf,
|
|
1316
|
+
view: getView,
|
|
1317
|
+
viewTiers: function() {
|
|
1318
|
+
return tiersFor(curView).map(function(tier) { return tier.key; });
|
|
1319
|
+
},
|
|
1320
|
+
breakpoints: BREAKPOINTS.map(function(tier) { return tier.key; }),
|
|
1321
|
+
getUtility: getUtility,
|
|
1322
|
+
setUtility: setUtility,
|
|
1323
|
+
utilityField: utilityField,
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/* --------------------------------------------------------------
|
|
1328
|
+
* Utilities: Bootstrap's responsive utility classes.
|
|
1329
|
+
*
|
|
1330
|
+
* A utility plugin declares families - order, d, justify-content -
|
|
1331
|
+
* and this is everything that reads or writes one. Every family is
|
|
1332
|
+
* spelled the way Bootstrap spells them all, {prefix}-{infix}-{value}
|
|
1333
|
+
* with no infix at the smallest tier, and cascades the way the size
|
|
1334
|
+
* classes do: a tier that says nothing takes the nearest smaller one.
|
|
1335
|
+
*
|
|
1336
|
+
* The canvas cannot show a breakpoint's utilities by itself. A
|
|
1337
|
+
* per-breakpoint view narrows the canvas, not the viewport, and
|
|
1338
|
+
* Bootstrap's utilities answer to the viewport with !important. So
|
|
1339
|
+
* each node gets what its classes mean at the view being edited,
|
|
1340
|
+
* as inline !important styles, recorded in an attribute so they come
|
|
1341
|
+
* off again - exactly those, leaving the host's own style alone.
|
|
1342
|
+
* -------------------------------------------------------------- */
|
|
1343
|
+
|
|
1344
|
+
var FAMILIES = {}; // Every family the utility plugins declare, by name
|
|
1345
|
+
var UTILITY_NODES = '.row, .column, .ge-element, [data-ge-container]';
|
|
1346
|
+
var PREVIEW_ATTR = 'data-ge-preview';
|
|
1347
|
+
var utilitiesOpen = false; // Whether the panels show their Responsive section unfolded
|
|
1348
|
+
|
|
1349
|
+
function registerFamily(pluginName, utility, family) {
|
|
1350
|
+
var name = family.name || family.prefix;
|
|
1351
|
+
|
|
1352
|
+
if (FAMILIES[name]) {
|
|
1353
|
+
warn('the "' + pluginName + '" plugin declares the utility "' + name +
|
|
1354
|
+
'", which the "' + FAMILIES[name].plugin + '" plugin already declared: ignored');
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
FAMILIES[name] = {
|
|
1359
|
+
plugin: pluginName,
|
|
1360
|
+
family: $.extend({}, family, {
|
|
1361
|
+
name: name,
|
|
1362
|
+
values: family.values.map(String),
|
|
1363
|
+
appliesTo: family.appliesTo || utility.appliesTo || ['row', 'column'],
|
|
1364
|
+
}),
|
|
1290
1365
|
};
|
|
1291
1366
|
}
|
|
1292
1367
|
|
|
1368
|
+
/** The family a caller named, or null and a warning when no plugin declares it. */
|
|
1369
|
+
function familyNamed(name, method) {
|
|
1370
|
+
if (FAMILIES[name]) { return FAMILIES[name].family; }
|
|
1371
|
+
|
|
1372
|
+
warnOnceHere('utility:' + name, method + '(' + JSON.stringify(name) + '): no loaded ' +
|
|
1373
|
+
'plugin declares that utility');
|
|
1374
|
+
return null;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* Whether a family applies to a kind of node. A container answers to
|
|
1379
|
+
* `container` and to its own type, so a plugin can take them all or
|
|
1380
|
+
* name the ones it means.
|
|
1381
|
+
*/
|
|
1382
|
+
function appliesTo(family, kind) {
|
|
1383
|
+
if (family.appliesTo.indexOf(kind) !== -1) { return true; }
|
|
1384
|
+
|
|
1385
|
+
return !!CONTAINERS[kind] && family.appliesTo.indexOf('container') !== -1;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
function familiesFor(kind) {
|
|
1389
|
+
return $.map(FAMILIES, function(entry) {
|
|
1390
|
+
return appliesTo(entry.family, kind) ? entry.family : null;
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
function utilityClass(family, tier, value) {
|
|
1395
|
+
return family.prefix + (tier.infix ? '-' + tier.infix : '') + '-' + value;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/** The value a node carries a class for at exactly this tier, or null. */
|
|
1399
|
+
function ownUtility(node, family, tier) {
|
|
1400
|
+
var classes = (node.attr('class') || '').split(/\s+/);
|
|
1401
|
+
|
|
1402
|
+
for (var i = 0; i < family.values.length; i++) {
|
|
1403
|
+
if (classes.indexOf(utilityClass(family, tier, family.values[i])) !== -1) {
|
|
1404
|
+
return family.values[i];
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
return null;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* The tier below this one that decides the value here, and what it
|
|
1413
|
+
* says, when this tier says nothing itself. Null when nothing below
|
|
1414
|
+
* says anything either.
|
|
1415
|
+
*/
|
|
1416
|
+
function inheritedUtility(node, family, tier) {
|
|
1417
|
+
for (var i = BREAKPOINTS.indexOf(tier) - 1; i >= 0; i--) {
|
|
1418
|
+
var value = ownUtility(node, family, BREAKPOINTS[i]);
|
|
1419
|
+
if (value !== null) { return { tier: BREAKPOINTS[i], value: value }; }
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
/** What applies at a tier: its own class, or what it inherits. */
|
|
1426
|
+
function effectiveUtility(node, family, tier) {
|
|
1427
|
+
var own = ownUtility(node, family, tier);
|
|
1428
|
+
if (own !== null) { return own; }
|
|
1429
|
+
|
|
1430
|
+
var inherited = inheritedUtility(node, family, tier);
|
|
1431
|
+
return inherited ? inherited.value : null;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/** Every tier the node carries a class of this family for. */
|
|
1435
|
+
function utilityTiers(node, family) {
|
|
1436
|
+
return BREAKPOINTS.map(function(tier) {
|
|
1437
|
+
return { tier: tier, value: ownUtility(node, family, tier) };
|
|
1438
|
+
}).filter(function(entry) { return entry.value !== null; });
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function writeUtility(node, family, tier, value) {
|
|
1442
|
+
family.values.forEach(function(candidate) {
|
|
1443
|
+
node.removeClass(utilityClass(family, tier, candidate));
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
if (value !== null) { node.addClass(utilityClass(family, tier, value)); }
|
|
1447
|
+
if (!node.attr('class')) { node.removeAttr('class'); }
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* What a view reads. A breakpoint reads what applies there. The all
|
|
1452
|
+
* view reads the class with no infix, since that is what it writes:
|
|
1453
|
+
* the one class that means "the same at every size".
|
|
1454
|
+
*/
|
|
1455
|
+
function readUtility(node, family, view) {
|
|
1456
|
+
return view === ALL_VIEW
|
|
1457
|
+
? ownUtility(node, family, BREAKPOINTS[0])
|
|
1458
|
+
: effectiveUtility(node, family, breakpoint(view));
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* What writing `value` in a view comes to, or null when it changes
|
|
1463
|
+
* nothing. A breakpoint writes its own tier and nothing else. The all
|
|
1464
|
+
* view writes the class with no infix and clears the family from
|
|
1465
|
+
* every other tier, and says what it cleared: choosing one value for
|
|
1466
|
+
* every size is choosing it over what the sizes said.
|
|
1467
|
+
*/
|
|
1468
|
+
function planUtility(node, family, view, value) {
|
|
1469
|
+
if (view !== ALL_VIEW) {
|
|
1470
|
+
var tier = breakpoint(view);
|
|
1471
|
+
if (ownUtility(node, family, tier) === value) { return null; }
|
|
1472
|
+
|
|
1473
|
+
return { writes: [{ tier: tier, value: value }], cleared: [] };
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
var writes = [];
|
|
1477
|
+
var cleared = [];
|
|
1478
|
+
|
|
1479
|
+
utilityTiers(node, family).forEach(function(entry) {
|
|
1480
|
+
if (entry.tier === BREAKPOINTS[0]) { return; }
|
|
1481
|
+
|
|
1482
|
+
writes.push({ tier: entry.tier, value: null });
|
|
1483
|
+
cleared.push({ breakpoint: entry.tier.key, value: entry.value });
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
if (ownUtility(node, family, BREAKPOINTS[0]) !== value) {
|
|
1487
|
+
writes.unshift({ tier: BREAKPOINTS[0], value: value });
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
return writes.length ? { writes: writes, cleared: cleared } : null;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
/** getUtility(node, family, view?): the value that applies, or null. */
|
|
1494
|
+
function getUtility(node, name, view) {
|
|
1495
|
+
node = $(node).first();
|
|
1496
|
+
|
|
1497
|
+
var family = familyNamed(name, 'getUtility');
|
|
1498
|
+
if (!family || !node.length) { return null; }
|
|
1499
|
+
|
|
1500
|
+
var key = view === undefined ? curView : viewKey(view);
|
|
1501
|
+
if (key === null) {
|
|
1502
|
+
warn('getUtility(' + JSON.stringify(view) + '): no such layout mode');
|
|
1503
|
+
return null;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
return readUtility(node, family, key);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* setUtility(node, family, value, view?): write a value through the
|
|
1511
|
+
* events. Null is "inherit". The last argument is a view key, or
|
|
1512
|
+
* { view, source } from a plugin that wants its tool named in the
|
|
1513
|
+
* payload. False when a handler canceled or nothing changed.
|
|
1514
|
+
*/
|
|
1515
|
+
function setUtility(node, name, value, options) {
|
|
1516
|
+
options = typeof options == 'string' ? { view: options } : (options || {});
|
|
1517
|
+
node = $(node).first();
|
|
1518
|
+
|
|
1519
|
+
var family = familyNamed(name, 'setUtility');
|
|
1520
|
+
if (!family || !node.length) { return false; }
|
|
1521
|
+
|
|
1522
|
+
var view = options.view === undefined ? curView : viewKey(options.view);
|
|
1523
|
+
if (view === null) {
|
|
1524
|
+
warn('setUtility(' + JSON.stringify(options.view) + '): no such layout mode');
|
|
1525
|
+
return false;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
value = value === null || value === undefined || value === '' ? null : String(value);
|
|
1529
|
+
if (value !== null && family.values.indexOf(value) === -1) {
|
|
1530
|
+
warn('setUtility: ' + JSON.stringify(value) + ' is not a value of "' + name +
|
|
1531
|
+
'", which takes ' + JSON.stringify(family.values));
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
var kind = kindOf(node);
|
|
1536
|
+
if (!appliesTo(family, kind)) {
|
|
1537
|
+
warn('setUtility: "' + name + '" does not apply to a ' + kind);
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
var plan = planUtility(node, family, view, value);
|
|
1542
|
+
if (!plan) { return false; }
|
|
1543
|
+
|
|
1544
|
+
return operate(function() {
|
|
1545
|
+
var payload = payloadFor(kind, node, {
|
|
1546
|
+
family: name,
|
|
1547
|
+
breakpoint: view,
|
|
1548
|
+
tiers: plan.writes.map(function(write) { return write.tier.key; }),
|
|
1549
|
+
from: readUtility(node, family, view),
|
|
1550
|
+
to: value,
|
|
1551
|
+
cleared: plan.cleared,
|
|
1552
|
+
source: options.source || 'api',
|
|
1553
|
+
});
|
|
1554
|
+
|
|
1555
|
+
if (!emit('before-utility', payload)) {
|
|
1556
|
+
// The panel already shows the value that was refused
|
|
1557
|
+
refreshUtilities(node);
|
|
1558
|
+
return false;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
plan.writes.forEach(function(write) {
|
|
1562
|
+
writeUtility(node, family, write.tier, write.value);
|
|
1563
|
+
});
|
|
1564
|
+
refreshUtilities(node);
|
|
1565
|
+
emit('after-utility', payload);
|
|
1566
|
+
|
|
1567
|
+
return true;
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
/** Bring a node's panel and preview up to date with its classes. */
|
|
1572
|
+
function refreshUtilities(node) {
|
|
1573
|
+
var details = node.children('.ge-tools-drawer').children('.ge-details');
|
|
1574
|
+
|
|
1575
|
+
details.children('.ge-classes').val(hostClasses(node).join(' '));
|
|
1576
|
+
details.children('.ge-utilities').each(function() { renderUtilities($(this)); });
|
|
1577
|
+
refreshPreviews(node);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/* Preview */
|
|
1581
|
+
|
|
1582
|
+
function utilityNodes(scope) {
|
|
1583
|
+
return scope.find(UTILITY_NODES).addBack(UTILITY_NODES);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* Put what each node's utilities mean at the view being edited on the
|
|
1588
|
+
* node itself. Only nodes that carry a class of a family get anything,
|
|
1589
|
+
* and they get the family's answer for the view even when that is
|
|
1590
|
+
* "nothing": a wider tier's class is still live in a wide window, and
|
|
1591
|
+
* has to be overruled.
|
|
1592
|
+
*
|
|
1593
|
+
* The all view gets nothing. Its canvas is not narrowed, every tier is
|
|
1594
|
+
* live, and what Bootstrap shows is the truth.
|
|
1595
|
+
*/
|
|
1596
|
+
function refreshPreviews(scope) {
|
|
1597
|
+
clearPreviews(scope);
|
|
1598
|
+
|
|
1599
|
+
if (curView !== ALL_VIEW) { previewTier(scope, breakpoint(curView)); }
|
|
1600
|
+
|
|
1601
|
+
// Whatever a plugin marks the canvas with to show its utilities
|
|
1602
|
+
// goes stale at the same moments the preview does
|
|
1603
|
+
plugins('onRefresh', scope);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
function previewTier(scope, tier) {
|
|
1607
|
+
utilityNodes(scope).each(function() {
|
|
1608
|
+
var node = $(this);
|
|
1609
|
+
var kind = kindOf(node);
|
|
1610
|
+
var styles = {};
|
|
1611
|
+
|
|
1612
|
+
familiesFor(kind).forEach(function(family) {
|
|
1613
|
+
if (!family.preview || !utilityTiers(node, family).length) { return; }
|
|
1614
|
+
|
|
1615
|
+
$.extend(styles, family.preview(effectiveUtility(node, family, tier), node, kind));
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
// A plugin whose families settle one property between them
|
|
1619
|
+
// previews the node as a whole
|
|
1620
|
+
$.each(UTILITIES, function(name, utility) {
|
|
1621
|
+
if (utility.preview) { $.extend(styles, utility.preview(node, kind, tier.key)); }
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
if (!$.isEmptyObject(styles)) { applyPreview(node, styles); }
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Set inline !important styles, which is the one thing that beats
|
|
1630
|
+
* Bootstrap's own !important, and remember what each property was so
|
|
1631
|
+
* it can be put back. The record is an attribute rather than jQuery
|
|
1632
|
+
* data because a rich text editor rebuilds the DOM of the area it
|
|
1633
|
+
* edits, and the record has to come back with the node.
|
|
1634
|
+
*/
|
|
1635
|
+
function applyPreview(node, styles) {
|
|
1636
|
+
var style = node[0].style;
|
|
1637
|
+
var was = {};
|
|
1638
|
+
|
|
1639
|
+
$.each(styles, function(property, value) {
|
|
1640
|
+
was[property] = [style.getPropertyValue(property), style.getPropertyPriority(property)];
|
|
1641
|
+
style.setProperty(property, String(value), 'important');
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
node.attr(PREVIEW_ATTR, JSON.stringify(was));
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function clearPreviews(scope) {
|
|
1648
|
+
scope.find('[' + PREVIEW_ATTR + ']').addBack('[' + PREVIEW_ATTR + ']').each(function() {
|
|
1649
|
+
var node = $(this);
|
|
1650
|
+
var style = this.style;
|
|
1651
|
+
var was = {};
|
|
1652
|
+
|
|
1653
|
+
try { was = JSON.parse(node.attr(PREVIEW_ATTR)) || {}; } catch (error) { /* a mangled record: drop it */ }
|
|
1654
|
+
|
|
1655
|
+
$.each(was, function(property, before) {
|
|
1656
|
+
if (before && before[0]) {
|
|
1657
|
+
style.setProperty(property, before[0], before[1]);
|
|
1658
|
+
} else {
|
|
1659
|
+
style.removeProperty(property);
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
node.removeAttr(PREVIEW_ATTR);
|
|
1664
|
+
if (!node.attr('style')) { node.removeAttr('style'); }
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/* The panel */
|
|
1669
|
+
|
|
1670
|
+
/**
|
|
1671
|
+
* The Responsive section of a node's settings panel: one field per
|
|
1672
|
+
* family that applies to the node, reading and writing the view being
|
|
1673
|
+
* edited, and whatever fields a plugin builds itself. Folded until the
|
|
1674
|
+
* user unfolds one, and then unfolded on every node, since whoever
|
|
1675
|
+
* wanted it on one wants it on the next.
|
|
1676
|
+
*/
|
|
1677
|
+
function createUtilitiesSection(node) {
|
|
1678
|
+
var kind = kindOf(node);
|
|
1679
|
+
var fields = familiesFor(kind)
|
|
1680
|
+
.filter(function(family) { return family.panel !== false; })
|
|
1681
|
+
.map(function(family) { return createField(node, family); });
|
|
1682
|
+
|
|
1683
|
+
$.each(UTILITIES, function(name, utility) {
|
|
1684
|
+
var own = utility.panel ? utility.panel(node, kind) : null;
|
|
1685
|
+
if (own && own.length) { fields.push(own); }
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
if (!fields.length) { return null; }
|
|
1689
|
+
|
|
1690
|
+
var section = $('<div class="ge-utilities" />')
|
|
1691
|
+
.toggleClass('ge-open', utilitiesOpen)
|
|
1692
|
+
.data('ge-node', node)
|
|
1693
|
+
;
|
|
1694
|
+
|
|
1695
|
+
$('<a class="ge-utilities-toggle" />')
|
|
1696
|
+
.appendTo(section)
|
|
1697
|
+
.on('click', function() {
|
|
1698
|
+
utilitiesOpen = !section.hasClass('ge-open');
|
|
1699
|
+
canvas.find('.ge-utilities').toggleClass('ge-open', utilitiesOpen);
|
|
1700
|
+
})
|
|
1701
|
+
;
|
|
1702
|
+
|
|
1703
|
+
var body = $('<div class="ge-utilities-body" />').appendTo(section);
|
|
1704
|
+
fields.forEach(function(field) { field.appendTo(body); });
|
|
1705
|
+
|
|
1706
|
+
renderUtilities(section);
|
|
1707
|
+
|
|
1708
|
+
return section;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/**
|
|
1712
|
+
* One family's field: its label, a select that writes through
|
|
1713
|
+
* setUtility, and a note. Filled by renderField, again whenever the
|
|
1714
|
+
* view or the node's classes change.
|
|
1715
|
+
*/
|
|
1716
|
+
function createField(node, family) {
|
|
1717
|
+
var field = $('<label class="ge-utility" />').attr('data-ge-family', family.name);
|
|
1718
|
+
|
|
1719
|
+
$('<span class="ge-utility-label" />')
|
|
1720
|
+
.text(family.labelKey ? t(family.labelKey) : family.name)
|
|
1721
|
+
.appendTo(field)
|
|
1722
|
+
;
|
|
1723
|
+
$('<select />')
|
|
1724
|
+
.appendTo(field)
|
|
1725
|
+
.on('change', function() {
|
|
1726
|
+
setUtility(node, family.name, this.value, { source: 'panel' });
|
|
1727
|
+
})
|
|
1728
|
+
;
|
|
1729
|
+
$('<small class="ge-utility-note" />').appendTo(field);
|
|
1730
|
+
|
|
1731
|
+
return field;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
/** ge.utilityField(node, family): a field a plugin places in a panel of its own. */
|
|
1735
|
+
function utilityField(node, name) {
|
|
1736
|
+
var family = familyNamed(name, 'utilityField');
|
|
1737
|
+
if (!family) { return $(); }
|
|
1738
|
+
|
|
1739
|
+
var field = createField(node, family);
|
|
1740
|
+
renderField(field, node);
|
|
1741
|
+
|
|
1742
|
+
return field;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/** Fill a section's fields, the families' and the plugins' own, for the view being edited. */
|
|
1746
|
+
function renderUtilities(section) {
|
|
1747
|
+
var node = section.data('ge-node');
|
|
1748
|
+
|
|
1749
|
+
section.children('.ge-utilities-toggle')
|
|
1750
|
+
.text(t('utility.section', { view: t(labelKeyFor(curView)) }));
|
|
1751
|
+
|
|
1752
|
+
section.find('.ge-utility').each(function() { renderField($(this), node); });
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
function renderField(field, node) {
|
|
1756
|
+
var family = FAMILIES[field.attr('data-ge-family')].family;
|
|
1757
|
+
var select = field.children('select').empty();
|
|
1758
|
+
var choices = family.choices ? family.choices(node, kindOf(node)) : family.values;
|
|
1759
|
+
var own, blank, note = '';
|
|
1760
|
+
|
|
1761
|
+
if (curView === ALL_VIEW) {
|
|
1762
|
+
own = ownUtility(node, family, BREAKPOINTS[0]);
|
|
1763
|
+
blank = t('utility.default');
|
|
1764
|
+
|
|
1765
|
+
var varies = utilityTiers(node, family).filter(function(entry) {
|
|
1766
|
+
return entry.tier !== BREAKPOINTS[0];
|
|
1767
|
+
});
|
|
1768
|
+
if (varies.length) {
|
|
1769
|
+
note = t('utility.varies', {
|
|
1770
|
+
breakpoints: varies.map(function(entry) { return entry.tier.key; }).join(', '),
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
} else {
|
|
1774
|
+
var tier = breakpoint(curView);
|
|
1775
|
+
var inherited = inheritedUtility(node, family, tier);
|
|
1776
|
+
|
|
1777
|
+
own = ownUtility(node, family, tier);
|
|
1778
|
+
blank = inherited
|
|
1779
|
+
? t('utility.inherit', { value: labelOf(family, inherited.value), breakpoint: inherited.tier.key })
|
|
1780
|
+
: t('utility.default');
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// A value the markup carries is shown even when the family
|
|
1784
|
+
// would not offer it here, rather than shown as something else
|
|
1785
|
+
if (own !== null && choices.indexOf(own) === -1) { choices = choices.concat([own]); }
|
|
1786
|
+
|
|
1787
|
+
$('<option value="" />').text(blank).appendTo(select);
|
|
1788
|
+
choices.forEach(function(value) {
|
|
1789
|
+
$('<option />').attr('value', value).text(labelOf(family, value)).appendTo(select);
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
select.val(own === null ? '' : own);
|
|
1793
|
+
field.children('.ge-utility-note').text(note).toggle(note !== '');
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
function labelOf(family, value) {
|
|
1797
|
+
return family.label ? family.label(value) : value;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1293
1800
|
/* --------------------------------------------------------------
|
|
1294
1801
|
* Containers: tabs, accordions and popups.
|
|
1295
1802
|
*
|
|
@@ -1305,6 +1812,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1305
1812
|
|
|
1306
1813
|
var CONTAINERS = {}; // The container plugins in use, by the type each builds
|
|
1307
1814
|
var FEATURES = {}; // The feature plugins in use, by name
|
|
1815
|
+
var UTILITIES = {}; // The utility plugins in use, by name
|
|
1308
1816
|
var featureMethods = {}; // The methods those features contribute
|
|
1309
1817
|
var containerCounter = 0;
|
|
1310
1818
|
|
|
@@ -1873,6 +2381,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1873
2381
|
details.toggle();
|
|
1874
2382
|
});
|
|
1875
2383
|
|
|
2384
|
+
// Beside the gear, because every node that has one is a node a
|
|
2385
|
+
// utility may apply to, whichever plugin built its drawer
|
|
2386
|
+
var kind = kindOf(node);
|
|
2387
|
+
$.each(UTILITIES, function(name, utility) {
|
|
2388
|
+
if (utility.drawerTools) { utility.drawerTools(drawer, node, kind); }
|
|
2389
|
+
});
|
|
2390
|
+
|
|
1876
2391
|
return details.appendTo(drawer);
|
|
1877
2392
|
}
|
|
1878
2393
|
|
|
@@ -1901,6 +2416,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1901
2416
|
.appendTo(detailsDiv)
|
|
1902
2417
|
.on('change', function() {
|
|
1903
2418
|
setHostClasses(container, this.value);
|
|
2419
|
+
refreshUtilities(container);
|
|
1904
2420
|
})
|
|
1905
2421
|
;
|
|
1906
2422
|
|
|
@@ -1913,11 +2429,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1913
2429
|
.on('click', function() {
|
|
1914
2430
|
btn.toggleClass('active btn-primary');
|
|
1915
2431
|
container.toggleClass(rowClass.cssClass, btn.hasClass('active'));
|
|
2432
|
+
refreshUtilities(container);
|
|
1916
2433
|
})
|
|
1917
2434
|
.appendTo(classGroup)
|
|
1918
2435
|
;
|
|
1919
2436
|
});
|
|
1920
2437
|
|
|
2438
|
+
var utilities = createUtilitiesSection(container);
|
|
2439
|
+
if (utilities) { utilities.appendTo(detailsDiv); }
|
|
2440
|
+
|
|
1921
2441
|
return detailsDiv;
|
|
1922
2442
|
}
|
|
1923
2443
|
|
|
@@ -2758,7 +3278,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2758
3278
|
return;
|
|
2759
3279
|
}
|
|
2760
3280
|
|
|
3281
|
+
var from = curView;
|
|
2761
3282
|
switchLayout(key);
|
|
3283
|
+
|
|
3284
|
+
if (key === from) { return; }
|
|
3285
|
+
|
|
3286
|
+
canvas.find('.ge-utilities').each(function() { renderUtilities($(this)); });
|
|
3287
|
+
refreshPreviews(canvas);
|
|
3288
|
+
plugins('onViewChange', key);
|
|
3289
|
+
emit('view-change', { canvas: canvas, breakpoint: key, from: from, to: key });
|
|
2762
3290
|
}
|
|
2763
3291
|
|
|
2764
3292
|
function getView() {
|
|
@@ -2802,6 +3330,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2802
3330
|
return addPaneTo(container, 'accordion', options);
|
|
2803
3331
|
},
|
|
2804
3332
|
setLocale: setLocale,
|
|
3333
|
+
getUtility: getUtility,
|
|
3334
|
+
setUtility: setUtility,
|
|
2805
3335
|
canvas: canvas,
|
|
2806
3336
|
settings: settingsCopy(),
|
|
2807
3337
|
};
|
|
@@ -2853,6 +3383,18 @@ $.fn.gridEditor.containers = {};
|
|
|
2853
3383
|
*/
|
|
2854
3384
|
$.fn.gridEditor.features = {};
|
|
2855
3385
|
|
|
3386
|
+
/**
|
|
3387
|
+
* Utility plugins: Bootstrap's responsive utility classes - order-md-2,
|
|
3388
|
+
* d-lg-none - edited per breakpoint. A plugin declares families of classes
|
|
3389
|
+
* and the editor reads them, writes them, puts them in the settings panel and
|
|
3390
|
+
* previews them in each view. Same factory, same `plugins` setting.
|
|
3391
|
+
*
|
|
3392
|
+
* $.fn.gridEditor.utilities.order = function(ge) {
|
|
3393
|
+
* return { families: [{ name: 'order', prefix: 'order', values: [...] }] };
|
|
3394
|
+
* };
|
|
3395
|
+
*/
|
|
3396
|
+
$.fn.gridEditor.utilities = {};
|
|
3397
|
+
|
|
2856
3398
|
/** Translator for the editor integrations, which get settings and no instance. */
|
|
2857
3399
|
$.fn.gridEditor.t = translate;
|
|
2858
3400
|
|
|
@@ -2904,6 +3446,10 @@ $.fn.gridEditor.locales = {
|
|
|
2904
3446
|
'view.lg': 'Desktop',
|
|
2905
3447
|
'view.xl': 'Large desktop',
|
|
2906
3448
|
'view.xxl': 'Widescreen',
|
|
3449
|
+
'utility.section': 'Responsive: {view}',
|
|
3450
|
+
'utility.default': 'Default',
|
|
3451
|
+
'utility.inherit': 'Inherit: {value} (from {breakpoint})',
|
|
3452
|
+
'utility.varies': 'Changes at {breakpoints}; choosing here replaces that',
|
|
2907
3453
|
'error.sortable_missing': 'SortableJS not available! Make sure you loaded the Sortable js file; dragging is off without it.',
|
|
2908
3454
|
'warning.setting_removed': 'The {setting} setting was removed in 4.0. Use {replacement} instead.',
|
|
2909
3455
|
'error.tinymce_missing': 'tinyMCE not available! Make sure you loaded the tinyMCE js file.',
|