form-builder.js 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. /*
  2. formBuilder - git@github.com:kevinchappell/formBuilder.git
  3. Version: 1.6.2
  4. Author: Kevin Chappell <kevin.b.chappell@gmail.com>
  5. */
  6. 'use strict';
  7. (function($) {
  8. 'use strict';
  9. var Toggle = function Toggle(element, options) {
  10. var defaults = {
  11. theme: 'fresh',
  12. labels: {
  13. off: 'Off',
  14. on: 'On'
  15. }
  16. };
  17. var opts = $.extend(defaults, options),
  18. $kcToggle = $('<div class="kc-toggle"/>').insertAfter(element).append(element);
  19. $kcToggle.toggleClass('on', element.is(':checked'));
  20. var kctOn = '<div class="kct-on">' + opts.labels.on + '</div>',
  21. kctOff = '<div class="kct-off">' + opts.labels.off + '</div>',
  22. kctHandle = '<div class="kct-handle"></div>',
  23. kctInner = '<div class="kct-inner">' + kctOn + kctHandle + kctOff + '</div>';
  24. $kcToggle.append(kctInner);
  25. $kcToggle.click(function() {
  26. element.attr('checked', !element.attr('checked'));
  27. $(this).toggleClass('on');
  28. });
  29. };
  30. $.fn.kcToggle = function(options) {
  31. var toggle = this;
  32. return toggle.each(function() {
  33. var element = $(this);
  34. if (element.data('kcToggle')) {
  35. return;
  36. }
  37. var kcToggle = new Toggle(element, options);
  38. element.data('kcToggle', kcToggle);
  39. });
  40. };
  41. })(jQuery);
  42. 'use strict';
  43. (function($) {
  44. 'use strict';
  45. var FormBuilder = function FormBuilder(element, options) {
  46. var defaults = {
  47. // Uneditable fields or other content you would like to
  48. // appear before and after regular fields.
  49. disableFields: {
  50. // before: '<h2>Header</h2>',
  51. // after: '<h3>Footer</h3>'
  52. },
  53. // array of objects with fields values
  54. // ex:
  55. // defaultFields: [{
  56. // label: 'First Name',
  57. // name: 'first-name',
  58. // required: 'true',
  59. // description: 'Your first name',
  60. // type: 'text'
  61. // }, {
  62. // label: 'Phone',
  63. // name: 'phone',
  64. // description: 'How can we reach you?',
  65. // type: 'text'
  66. // }],
  67. defaultFields: [],
  68. roles: {
  69. 1 : 'Administrator'
  70. },
  71. showWarning: false,
  72. serializePrefix: 'frmb',
  73. messages: {
  74. add: 'Add Item',
  75. allowSelect: 'Allow Select',
  76. autocomplete: 'Autocomplete',
  77. cannotBeEmpty: 'This field cannot be empty',
  78. checkboxGroup: 'Checkbox Group',
  79. checkbox: 'Checkbox',
  80. checkboxes: 'Checkboxes',
  81. clearAllMessage: 'Are you sure you want to remove all items?',
  82. clearAll: 'Clear All',
  83. close: 'Close',
  84. copy: 'Copy To Clipboard',
  85. dateField: 'Date Field',
  86. description: 'Help Text',
  87. descriptionField: 'Description',
  88. devMode: 'Developer Mode',
  89. disableFields: 'These fields cannot be moved.',
  90. editNames: 'Edit Names',
  91. editorTitle: 'Form Elements',
  92. editXML: 'Edit XML',
  93. fieldVars: 'Field Variables',
  94. fieldRemoveWarning: 'Are you sure you want to remove this field?',
  95. getStarted: 'Drag a field from the right to this area',
  96. hide: 'Edit',
  97. hidden: 'Hidden Input',
  98. label: 'Label',
  99. labelEmpty: 'Field Label cannot be empty',
  100. limitRole: 'Limit access to one or more of the following roles:',
  101. mandatory: 'Mandatory',
  102. maxLength: 'Max Length',
  103. minOptionMessage: 'This field requires a minimum of 2 options',
  104. name: 'Name',
  105. no: 'No',
  106. off: 'Off',
  107. on: 'On',
  108. optional: 'optional',
  109. optionLabelPlaceholder: 'Label',
  110. optionValuePlaceholder: 'Value',
  111. optionEmpty: 'Option value required',
  112. paragraph: 'Paragraph',
  113. preview: 'Preview',
  114. radioGroup: 'Radio Group',
  115. radio: 'Radio',
  116. removeMessage: 'Remove Element',
  117. remove: '&#215;',
  118. required: 'Required',
  119. richText: 'Rich Text Editor',
  120. roles: 'Access',
  121. save: 'Save Template',
  122. selectOptions: 'Select Items',
  123. select: 'Select',
  124. selectionsMessage: 'Allow Multiple Selections',
  125. text: 'Text Field',
  126. textLabel: 'Title',
  127. textIntroduce: 'Description',
  128. toggle: 'Toggle',
  129. warning: 'Warning!',
  130. viewXML: 'View XML',
  131. yes: 'Yes',
  132. defaultValue : 'Default Value',
  133. }
  134. };
  135. var startIndex, doCancel, _helpers = {};
  136. /**
  137. * Callback for when a drag begins
  138. * @param {object} event
  139. * @param {object} ui
  140. */
  141. _helpers.startMoving = function(event, ui) {
  142. event = event;
  143. ui.item.addClass('moving');
  144. startIndex = $('li', this).index(ui.item);
  145. };
  146. /**
  147. * Callback for when a drag ends
  148. * @param {object} event
  149. * @param {object} ui
  150. */
  151. _helpers.stopMoving = function(event, ui) {
  152. event = event;
  153. ui.item.removeClass('moving');
  154. if (doCancel) {
  155. $(ui.sender).sortable('cancel');
  156. $(this).sortable('cancel');
  157. }
  158. };
  159. /**
  160. * Make strings safe to be used as classes
  161. * @param {string} str string to be converted
  162. * @return {string} converter string
  163. */
  164. _helpers.safename = function(str) {
  165. return str.replace(/\s/g, '-').replace(/[^a-zA-Z0-9\-\_]/g, '').toLowerCase();
  166. };
  167. /**
  168. * Strips non-numbers from a number only input
  169. * @param {string} str string with possible number
  170. * @return {string} string without numbers
  171. */
  172. _helpers.forceNumber = function(str) {
  173. return str.replace(/[^0-9]/g, '');
  174. };
  175. /**
  176. * hide and show mouse tracking tooltips, only used for disabled
  177. * fields in the editor.
  178. * @todo remove or refactor to make better use
  179. * @param {object} tt jQuery option with nexted tooltip
  180. * @return {void}
  181. */
  182. _helpers.initTooltip = function(tt) {
  183. var tooltip = tt.find('.tooltip');
  184. tt.mouseenter(function() {
  185. if (tooltip.outerWidth() > 200) {
  186. tooltip.addClass('max-width');
  187. }
  188. tooltip.css('left', tt.width() + 14);
  189. tooltip.stop(true, true).fadeIn('fast');
  190. }).mouseleave(function() {
  191. tt.find('.tooltip').stop(true, true).fadeOut('fast');
  192. });
  193. tooltip.hide();
  194. };
  195. // saves the field data to our canvas (elem)
  196. _helpers.save = function() {
  197. $sortableFields.children('li').not('.disabled').each(function() {
  198. _helpers.updatePreview($(this));
  199. });
  200. elem.val($sortableFields.toXML());
  201. };
  202. // updatePreview will generate the preview for radio and checkbox groups
  203. _helpers.updatePreview = function(field) {
  204. var fieldClass = field.attr('class'),
  205. $prevHolder = $('.prev-holder', field);
  206. if (fieldClass.indexOf('ui-sortable-handle') !== -1) {
  207. return;
  208. }
  209. fieldClass = fieldClass.replace(' form-field', '');
  210. var preview, previewData = {
  211. type: fieldClass,
  212. label: $('.fld-label', field).val()
  213. };
  214. if (fieldClass === 'checkbox') {
  215. previewData.toggle = $('.checkbox-toggle', field).is(':checked');
  216. }
  217. if (fieldClass.match(/(select|checkbox-group|radio-group)/)) {
  218. previewData.values = [];
  219. $('.sortable-options li', field).each(function() {
  220. var option = {};
  221. option.selected = $('.select-option', $(this)).is(':checked');
  222. option.value = $('.option-value', $(this)).val();
  223. option.label = $('.option-label', $(this)).val();
  224. previewData.values.push(option);
  225. });
  226. }
  227. preview = fieldPreview(previewData);
  228. $prevHolder.html(preview);
  229. $('input[toggle]', $prevHolder).kcToggle();
  230. };
  231. // update preview to label
  232. _helpers.updateMultipleSelect = function() {
  233. $sortableFields.delegate('input[name="multiple"]', 'change',
  234. function() {
  235. var options = $(this).parents('.fields:eq(0)').find('.sortable-options input.select-option');
  236. if (this.checked) {
  237. options.each(function() {
  238. $(this).prop('type', 'checkbox');
  239. });
  240. } else {
  241. options.each(function() {
  242. $(this).removeAttr('checked').prop('type', 'radio');
  243. });
  244. }
  245. });
  246. };
  247. _helpers.htmlEncode = function(value) {
  248. return $('<div/>').text(value).html();
  249. };
  250. _helpers.htmlDecode = function(value) {
  251. return $('<div/>').html(value).text();
  252. };
  253. _helpers.validateForm = function() {
  254. var errors = [];
  255. // check for empty field labels
  256. $('input[name="label"], input[type="text"].option', $sortableFields).each(function() {
  257. if ($(this).val() === '') {
  258. var field = $(this).parents('li.form-field'),
  259. fieldAttr = $(this);
  260. errors.push({
  261. field: field,
  262. error: opts.messages.labelEmpty,
  263. attribute: fieldAttr
  264. });
  265. }
  266. });
  267. // @todo add error = { noVal: opts.messages.labelEmpty }
  268. if (errors.length) {
  269. alert('Error: ' + errors[0].error);
  270. $('html, body').animate({
  271. scrollTop: errors[0].field.offset().top
  272. },
  273. 1000,
  274. function() {
  275. var targetID = $('.toggle-form', errors[0].field).attr('id');
  276. $('.toggle-form', errors[0].field).addClass('open').parent().next('.prev-holder').slideUp(250);
  277. $('#' + targetID + '-fld').slideDown(250,
  278. function() {
  279. errors[0].attribute.addClass('error');
  280. });
  281. });
  282. }
  283. };
  284. _helpers.disabledTT = function(field) {
  285. var title = field.attr('data-tooltip');
  286. if (title) {
  287. field.removeAttr('title').data('tip_text', title);
  288. var tt = $('<p/>', {
  289. 'class': 'frmb-tt'
  290. }).html(title);
  291. field.append(tt);
  292. tt.css({
  293. top: -tt.outerHeight(),
  294. left: -15
  295. });
  296. field.mouseleave(function() {
  297. $(this).attr('data-tooltip', field.data('tip_text'));
  298. $('.frmb-tt').remove();
  299. });
  300. }
  301. };
  302. var opts = $.extend(defaults, options),
  303. elem = $(element),
  304. frmbID = 'frmb-' + $('ul[id^=frmb-]').length++;
  305. var field = '',
  306. lastID = 1,
  307. boxID = frmbID + '-control-box';
  308. // create array of field objects to cycle through
  309. var frmbFields = [{
  310. label: opts.messages.text,
  311. attrs: {
  312. type: 'text',
  313. className: 'text-input',
  314. name: 'text-input'
  315. }
  316. },
  317. {
  318. label: opts.messages.select,
  319. attrs: {
  320. type: 'select',
  321. className: 'select',
  322. name: 'select'
  323. }
  324. },
  325. {
  326. label: opts.messages.richText,
  327. attrs: {
  328. type: 'rich-text',
  329. className: 'rich-text',
  330. name: 'rich-text'
  331. }
  332. },
  333. {
  334. label: opts.messages.radioGroup,
  335. attrs: {
  336. type: 'radio-group',
  337. className: 'radio-group',
  338. name: 'radio-group'
  339. }
  340. },
  341. {
  342. label: opts.messages.hidden,
  343. attrs: {
  344. type: 'hidden',
  345. className: 'hidden-input',
  346. name: 'hidden-input'
  347. }
  348. },
  349. {
  350. label: opts.messages.dateField,
  351. attrs: {
  352. type: 'date',
  353. className: 'calendar',
  354. name: 'date-input'
  355. }
  356. },
  357. {
  358. label: opts.messages.checkboxGroup,
  359. attrs: {
  360. type: 'checkbox-group',
  361. className: 'checkbox-group',
  362. name: 'checkbox-group'
  363. }
  364. },
  365. {
  366. label: opts.messages.checkbox,
  367. attrs: {
  368. type: 'checkbox',
  369. className: 'checkbox',
  370. name: 'checkbox'
  371. }
  372. },
  373. {
  374. label: opts.messages.autocomplete,
  375. attrs: {
  376. type: 'autocomplete',
  377. className: 'autocomplete',
  378. name: 'autocomplete'
  379. }
  380. },
  381. {
  382. label: opts.messages.textLabel,
  383. attrs: {
  384. type: 'text-label',
  385. className: 'text-label',
  386. name: 'text'
  387. }
  388. }];
  389. // Create draggable fields for formBuilder
  390. var cbUL = $('<ul/>', {
  391. id: boxID,
  392. 'class': 'frmb-control'
  393. });
  394. // Loop through
  395. for (var i = frmbFields.length - 1; i >= 0; i--) {
  396. var $field = $('<li/>', {
  397. 'class': 'icon-' + frmbFields[i].attrs.className,
  398. 'type': frmbFields[i].type,
  399. 'name': frmbFields[i].className,
  400. 'label': frmbFields[i].label
  401. });
  402. for (var attr in frmbFields[i]) {
  403. if (frmbFields[i].hasOwnProperty(attr)) {
  404. $field.data(attr, frmbFields[i][attr]);
  405. }
  406. }
  407. $field.html(frmbFields[i].label).appendTo(cbUL);
  408. }
  409. // Build our headers and action links
  410. var cbHeader = $('<h4/>').html(opts.messages.editorTitle),
  411. viewXML = $('<a/>', {
  412. id: frmbID + '-export-xml',
  413. text: opts.messages.viewXML,
  414. href: '#',
  415. 'class': 'view-xml'
  416. }),
  417. allowSelect = $('<a/>', {
  418. id: frmbID + '-allow-select',
  419. text: opts.messages.allowSelect,
  420. href: '#',
  421. 'class': 'allow-select'
  422. }).prop('checked', 'checked'),
  423. editXML = $('<a/>', {
  424. id: frmbID + '-edit-xml',
  425. text: opts.messages.editXML,
  426. href: '#',
  427. 'class': 'edit-xml'
  428. }),
  429. editNames = $('<a/>', {
  430. id: frmbID + '-edit-names',
  431. text: opts.messages.editNames,
  432. href: '#',
  433. 'class': 'edit-names'
  434. }),
  435. clearAll = $('<a/>', {
  436. id: frmbID + '-clear-all',
  437. text: opts.messages.clearAll,
  438. href: '#',
  439. 'class': 'clear-all'
  440. }),
  441. saveAll = $('<div/>', {
  442. id: frmbID + '-save',
  443. href: '#',
  444. 'class': 'save-btn-wrap',
  445. title: opts.messages.save
  446. }).html('<a class="save fb-button primary"><span>' + opts.messages.save + '</span></a>'),
  447. actionLinksInner = $('<div/>', {
  448. id: frmbID + '-action-links-inner',
  449. 'class': 'action-links-inner'
  450. }).append(editXML, ' | ', editNames, ' | ', allowSelect, ' | ', clearAll, ' |&nbsp;'),
  451. devMode = $('<span/>', {
  452. 'class': 'dev-mode-link'
  453. }).html(opts.messages.devMode + ' ' + opts.messages.off),
  454. actionLinks = $('<div/>', {
  455. id: frmbID + '-action-links',
  456. 'class': 'action-links'
  457. }).append(actionLinksInner, devMode);
  458. // Sortable fields
  459. var $sortableFields = $('<ul/>').attr('id', frmbID).addClass('frmb').sortable({
  460. cursor: 'move',
  461. opacity: 0.9,
  462. beforeStop: function beforeStop(event, ui) {
  463. var lastIndex = $('> li', $sortableFields).length - 1,
  464. curIndex = ui.placeholder.index();
  465. doCancel = curIndex <= 1 || curIndex === lastIndex;
  466. },
  467. start: _helpers.startMoving,
  468. stop: _helpers.stopMoving,
  469. cancel: 'input, .disabled, .sortable-options, .add, .btn, .no-drag',
  470. // items: 'li:not(.no-fields)',
  471. receive: function receive(event, ui) {
  472. // if (doCancel) {
  473. // $('li:nth-child(' + curIndex + ')', $(this)).remove();
  474. // }
  475. },
  476. placeholder: 'frmb-placeholder'
  477. });
  478. // ControlBox with different fields
  479. cbUL.sortable({
  480. helper: 'clone',
  481. opacity: 0.9,
  482. connectWith: $sortableFields,
  483. cursor: 'move',
  484. placeholder: 'ui-state-highlight',
  485. start: _helpers.startMoving,
  486. stop: _helpers.stopMoving,
  487. revert: 150,
  488. change: function change(event, ui) {
  489. //fix the logic on this to only hide placeholder for disabledFields.before and after
  490. // if (ui.placeholder.index() === 0 || ui.placeholder.index() === $('> li', $sortableFields).last().index()) {
  491. // $(ui.placeholder).hide();
  492. // } else {
  493. // $(ui.placeholder).show();
  494. // }
  495. },
  496. remove: function remove(event, ui) {
  497. if (startIndex === 0) {
  498. cbUL.prepend(ui.item);
  499. } else {
  500. $('li:nth-child(' + startIndex + ')', cbUL).after(ui.item);
  501. }
  502. },
  503. beforeStop: function beforeStop(event, ui) {
  504. var lastIndex = $('> li', $sortableFields).length - 1,
  505. curIndex = ui.placeholder.index();
  506. doCancel = curIndex <= 1 || curIndex === lastIndex ? true: false;
  507. if (ui.placeholder.parent().hasClass('frmb-control')) {
  508. doCancel = true;
  509. }
  510. },
  511. update: function update(event, ui) {
  512. // _helpers.stopMoving;
  513. elem.stopIndex = $('li', $sortableFields).index(ui.item) === 0 ? '0': $('li', $sortableFields).index(ui.item);
  514. if ($('li', $sortableFields).index(ui.item) < 0) {
  515. $(this).sortable('cancel');
  516. } else {
  517. prepFieldVars($(ui.item[0]), true);
  518. }
  519. },
  520. receive: function receive(event, ui) {
  521. if (ui.sender.hasClass('frmb') || ui.sender.hasClass('frmb-control')) {
  522. $(ui.sender).sortable('cancel');
  523. }
  524. }
  525. });
  526. var $stageWrap = $('<div/>', {
  527. id: frmbID + '-stage-wrap',
  528. 'class': 'stage-wrap'
  529. });
  530. var $formWrap = $('<div/>', {
  531. id: frmbID + '-form-wrap',
  532. 'class': 'form-wrap'
  533. });
  534. elem.before($stageWrap).appendTo($stageWrap);
  535. // Replace the textarea with sortable list.
  536. //elem.before($sortableFields).parent().prepend(frmbHeader).addClass('frmb-wrap').append(actionLinks, viewXML, saveAll);
  537. var cbWrap = $('<div/>', {
  538. id: frmbID + '-cb-wrap',
  539. 'class': 'cb-wrap'
  540. }).append(cbHeader, cbUL);
  541. $stageWrap.append($sortableFields, cbWrap, actionLinks, viewXML, saveAll);
  542. $stageWrap.before($formWrap);
  543. $formWrap.append($stageWrap, cbWrap);
  544. var doSave = function doSave() {
  545. if ($(this).parents('li.disabled').length === 0) {
  546. if ($(this).name === 'label' && $(this).val() === '') {
  547. return alert('Error: ' + opts.messages.labelEmpty);
  548. }
  549. _helpers.save();
  550. }
  551. };
  552. // Not pretty but we need to save a lot so users don't have to keep clicking a save button
  553. $('input, select', $sortableFields).on('change', doSave);
  554. $('input, select', $sortableFields).on('blur', doSave);
  555. // Parse saved XML template data
  556. elem.getTemplate = function() {
  557. var xml = elem.val() !== '' ? $.parseXML(elem.val()) : false,
  558. fields = $(xml).find('field');
  559. if (fields.length > 0) {
  560. fields.each(function() {
  561. prepFieldVars($(this));
  562. });
  563. } else if (!xml) {
  564. // Load default fields if none are set
  565. if (opts.defaultFields.length) {
  566. for (var i = opts.defaultFields.length - 1; i >= 0; i--) {
  567. appendNewField(opts.defaultFields[i]);
  568. }
  569. } else {
  570. $stageWrap.addClass('empty').attr('data-content', opts.messages.getStarted);
  571. }
  572. disabledBeforeAfter();
  573. }
  574. };
  575. var disabledBeforeAfter = function disabledBeforeAfter() {
  576. var li = '<li class="disabled __POSITION__">__CONTENT__</li>';
  577. if (opts.disableFields.before && !$('.disabled.before', $sortableFields).length) {
  578. $sortableFields.prepend(li.replace('__POSITION__', 'before').replace('__CONTENT__', opts.disableFields.before));
  579. }
  580. if (opts.disableFields.after && !$('.disabled.after', $sortableFields).length) {
  581. $sortableFields.append(li.replace('__POSITION__', 'after').replace('__CONTENT__', opts.disableFields.after));
  582. }
  583. };
  584. var nameAttr = function nameAttr(field) {
  585. var epoch = new Date().getTime();
  586. return field.data('attrs').name + '-' + epoch;
  587. };
  588. var prepFieldVars = function prepFieldVars($field, isNew) {
  589. isNew = isNew || false;
  590. var fieldAttrs = $field.data('attrs') || {},
  591. fType = fieldAttrs.type || $field.attr('type'),
  592. isMultiple = fType.match(/(select|checkbox-group|radio-group)/),
  593. values = {};
  594. values.label = _helpers.htmlEncode($field.attr('label'));
  595. values.name = isNew ? nameAttr($field) : fieldAttrs.name || $field.attr('name');
  596. values.name = typeof values.name == 'undefined' ? '' : values.name;
  597. values.defaultVal = $field.attr('defaultVal') !== undefined ? _helpers.htmlEncode($field.attr('defaultVal')) : '';
  598. values.role = $field.attr('role');
  599. values.required = $field.attr('required');
  600. values.maxLength = $field.attr('max-length');
  601. values.toggle = $field.attr('toggle');
  602. values.type = fType;
  603. values.description = $field.attr('description') !== undefined ? _helpers.htmlEncode($field.attr('description')) : '';
  604. values.introduce = $field.attr('introduce') !== undefined ? _helpers.htmlEncode($field.attr('introduce')) : '';
  605. if (isMultiple) {
  606. values.multiple = true;
  607. values.values = [];
  608. $field.children().each(function(i) {
  609. var value = {
  610. label: $(this).text(),
  611. value: $(this).attr('value'),
  612. selected: $field.attr('default') === i ? true: false
  613. };
  614. values.values.push(value);
  615. });
  616. }
  617. appendNewField(values);
  618. $stageWrap.removeClass('empty');
  619. disabledBeforeAfter();
  620. };
  621. // multi-line textarea
  622. var appendTextarea = function appendTextarea(values) {
  623. appendFieldLi(opts.messages.richText, advFields(values), values);
  624. };
  625. var appendInput = function appendInput(values) {
  626. var type = values.type || 'text';
  627. appendFieldLi(opts.messages[type], advFields(values), values);
  628. };
  629. var textLabel = function textLabel(values) {
  630. var type = values.type || 'text';
  631. values.introduce = values.introduce || opts.messages.textIntroduce;
  632. appendFieldLi(opts.messages[type], advFields(values), values);
  633. }
  634. // add select dropdown
  635. var appendSelectList = function appendSelectList(values) {
  636. if (!values.values || !values.values.length) {
  637. values.values = [{
  638. selected: 'false',
  639. label: 'Option 1',
  640. value: 'option-1'
  641. },
  642. {
  643. selected: 'false',
  644. label: 'Option 2',
  645. value: 'option-2'
  646. }];
  647. }
  648. var field = '',
  649. name = _helpers.safename(values.name),
  650. multiDisplay = values.type === 'checkbox-group' ? 'none': 'none';
  651. field += advFields(values);
  652. field += '<div class="false-label">' + opts.messages.selectOptions + '</div>';
  653. field += '<div class="fields">';
  654. field += '<div class="allow-multi" style="display:' + multiDisplay + '">';
  655. field += '<input type="checkbox" id="multiple_' + lastID + '" name="multiple"' + (values.multiple ? 'checked="checked"': '') + '>';
  656. field += '<label class="multiple" for="multiple_' + lastID + '">' + opts.messages.selectionsMessage + '</label>';
  657. field += '</div>';
  658. field += '<ol class="sortable-options">';
  659. for (i = 0; i < values.values.length; i++) {
  660. field += selectFieldOptions(values.values[i], name, values.values[i].selected, values.multiple);
  661. }
  662. field += '</ol>';
  663. field += '<div class="field_actions"><a href="#" class="add add_opt"><strong>' + opts.messages.add + '</strong></a> | <a href="#" class="close_field">' + opts.messages.close + '</a></div>';
  664. field += '</div>';
  665. appendFieldLi(opts.messages.select, field, values);
  666. $('.sortable-options').sortable(); // making the dynamically added option fields sortable.
  667. };
  668. var appendNewField = function appendNewField(values) {
  669. if (values === undefined) {
  670. values = '';
  671. }
  672. // TODO: refactor to move functions into this object
  673. var appendFieldType = {
  674. // 'text': appendTextInput(values),
  675. // 'checkbox': appendCheckbox(values),
  676. // 'select': appendSelectList(values),
  677. // 'textarea': appendTextarea(values),
  678. 'text-label': textLabel,
  679. '2': appendInput,
  680. 'date': appendInput,
  681. 'autocomplete': appendInput,
  682. 'checkbox': appendInput,
  683. 'select': appendSelectList,
  684. 'rich-text': appendTextarea,
  685. 'textarea': appendTextarea,
  686. 'radio-group': appendSelectList,
  687. 'checkbox-group': appendSelectList,
  688. 'text': appendInput,
  689. 'hidden': appendInput
  690. };
  691. if (typeof appendFieldType[values.type] === 'function') {
  692. appendFieldType[values.type](values);
  693. }
  694. };
  695. /**
  696. * Build the editable properties for the field
  697. * @param {object} values configuration object for advanced fields
  698. * @return {string} markup for advanced fields
  699. */
  700. var advFields = function advFields(values) {
  701. var advFields = '',
  702. key, roles = values.role !== undefined ? values.role.split(',') : [];
  703. var fieldLabel = $('<div>', {
  704. 'class': 'frm-fld label-wrap'
  705. });
  706. $('<label/>').html(opts.messages.label + ' *').appendTo(fieldLabel);
  707. $('<input>', {
  708. type: 'text',
  709. name: 'label',
  710. value: values.label,
  711. 'class': 'fld-label'
  712. }).appendTo(fieldLabel);
  713. advFields += fieldLabel[0].outerHTML;
  714. var fieldDesc = $('<div>', {
  715. 'class': 'frm-fld description-wrap'
  716. });
  717. $('<label/>').html(opts.messages.description + ' *').appendTo(fieldDesc);
  718. if (values.type == 'text-label') { //text-label
  719. advFields += '<div class="frm-fld description-wrap"><label>' + opts.messages.textIntroduce + '</label>';
  720. advFields += '<input type="text" name="introduce" value="' + values.introduce + '" class="fld-introduce" id="introduce-' + lastID + '" /></div>';
  721. }
  722. if (values.type != 'text-label') {
  723. advFields += '<div class="frm-fld description-wrap"><label>' + opts.messages.description + '</label>';
  724. advFields += '<input type="text" name="description" value="' + values.description + '" class="fld-description" id="description-' + lastID + '" /></div>';
  725. advFields += '<div class="frm-fld name-wrap"><label>' + opts.messages.name + ' <span class="required">*</span></label>';
  726. advFields += '<input type="text" name="name" value="' + values.name + '" class="fld-name" id="title-' + lastID + '" /></div>';
  727. }
  728. advFields += '<div class="frm-fld access-wrap"><label>' + opts.messages.roles + '</label>';
  729. advFields += '<input type="checkbox" name="enable_roles" value="" ' + (values.role !== undefined ? 'checked': '') + ' id="enable_roles-' + lastID + '"/> <label for="enable_roles-' + lastID + '" class="roles_label">' + opts.messages.limitRole + '</label>';
  730. advFields += '<div class="frm-fld available-roles" ' + (values.role !== undefined ? 'style="display:block"': '') + '>';
  731. for (key in opts.roles) {
  732. if ($.inArray(key, ['date', '4']) === -1) {
  733. advFields += '<input type="checkbox" name="roles[]" value="' + key + '" id="fld-' + lastID + '-roles-' + key + '" ' + ($.inArray(key, roles) !== -1 ? 'checked': '') + ' class="roles-field" /><label for="fld-' + lastID + '-roles-' + key + '">' + opts.roles[key] + '</label><br/>';
  734. }
  735. }
  736. advFields += '</div></div>';
  737. // if field type is not checkbox, checkbox/radio group or select list, add max length
  738. if ($.inArray(values.type, ['checkbox', 'select', 'checkbox-group', 'date', 'autocomplete', 'radio-group', 'text-label']) < 0) {
  739. advFields += '<div class="frm-fld"><label class="max-length-label">' + opts.messages.maxLength + '</label>';
  740. advFields += '<input type="text" name="max-length" max-length="4" value="' + (values.maxLength !== undefined ? values.maxLength: '') + '" class="fld-max-length" id="max-length-' + lastID + '" /></div>';
  741. }
  742. return advFields;
  743. };
  744. // Append the new field to the editor
  745. var appendFieldLi = function appendFieldLi(title, field, values) {
  746. var label = $(field).find('input[name="label"]').val() !== '' ? $(field).find('input[name="label"]').val() : title;
  747. var defaultVal = typeof values.defaultVal != 'undefined' ? values.defaultVal : '';
  748. var introduce = $(field).find('input[name="introduce"]').val() !== '' ? $(field).find('input[name="introduce"]').val() : '';
  749. var li = '',
  750. delBtn = '<a id="del_' + lastID + '" class="del-button btn delete-confirm" href="#" title="' + opts.messages.removeMessage + '">' + opts.messages.remove + '</a>',
  751. toggleBtn = '<a id="frm-' + lastID + '" class="toggle-form btn icon-pencil" href="#" title="' + opts.messages.hide + '"></a> ',
  752. required = values.required,
  753. toggle = values.toggle || undefined,
  754. tooltip = values.description !== '' ? '<span class="tooltip-element" tooltip="' + values.description + '">?</span>': '';
  755. li += '<li id="frm-' + lastID + '-item" class="' + values.type + ' form-field">';
  756. li += '<div class="legend">';
  757. li += delBtn;
  758. li += '<span id="txt-title-' + lastID + '" class="field-label">' + label + '</span>' + tooltip + '<span class="required-asterisk" ' + (required === 'true' ? 'style="display:inline"': '') + '> *</span>' + toggleBtn;
  759. li += '</div>';
  760. if (values.type == 'text-label') {
  761. li += '<div class="frm-fld">';
  762. li += '<label class="field-introduce">' + introduce + '</label>';
  763. li += '</div>';
  764. }
  765. li += '<div class="prev-holder">' + fieldPreview(values) + '</div>';
  766. li += '<div id="frm-' + lastID + '-fld" class="frm-holder">';
  767. li += '<div class="form-elements">';
  768. if (values.type != 'text-label') {
  769. li += '<div class="frm-fld">';
  770. li += '<label>&nbsp;</label>';
  771. li += '<input class="required" type="checkbox" value="1" name="required-' + lastID + '" id="required-' + lastID + '"' + (required === 'true' ? ' checked="checked"': '') + ' /><label class="required_label" for="required-' + lastID + '">' + opts.messages.required + '</label>';
  772. if (values.type === 'checkbox') {
  773. li += '<div class="frm-fld">';
  774. li += '<label>&nbsp;</label>';
  775. li += '<input class="checkbox-toggle" type="checkbox" value="1" name="toggle-' + lastID + '" id="toggle-' + lastID + '"' + (toggle === 'true' ? ' checked="checked"': '') + ' /><label class="toggle-label" for="toggle-' + lastID + '">' + opts.messages.toggle + '</label>';
  776. li += '</div>';
  777. }
  778. li += '</div>';
  779. }
  780. if (values.type == 'hidden') {
  781. li += '<div class="frm-fld">';
  782. li += '<label class="field-defaultVal">'+opts.messages.defaultValue+'</label>';
  783. li += '<input type="text" class="fld-defaultVal" name="defaultVal" id="defaultVal-'+lastID+'" value="'+ defaultVal +'" />';
  784. li += '</div>';
  785. }
  786. li += field;
  787. li += '</div>';
  788. li += '</div>';
  789. li += '</li>';
  790. if (elem.stopIndex) {
  791. $('li', $sortableFields).eq(elem.stopIndex).after(li);
  792. } else {
  793. $sortableFields.append(li);
  794. }
  795. $(document.getElementById('frm-' + lastID + '-item')).hide().slideDown(250);
  796. lastID++;
  797. _helpers.save();
  798. };
  799. /**
  800. * Generate preview markup
  801. * @param {object} attrs
  802. * @return {string} preview markup for field
  803. */
  804. var fieldPreview = function fieldPreview(attrs) {
  805. var i, preview = '',
  806. epoch = new Date().getTime();
  807. switch (attrs.type) {
  808. case 'textarea':
  809. preview = '<' + attrs.type + '></' + attrs.type + '>';
  810. break;
  811. case 'select':
  812. var options;
  813. attrs.values.reverse();
  814. for (i = attrs.values.length - 1; i >= 0; i--) {
  815. options += '<option value="' + attrs.values[i].value + '">' + attrs.values[i].label + '</option>';
  816. }
  817. preview = '<' + attrs.type + ' class="no-drag">' + options + '</' + attrs.type + '>';
  818. break;
  819. case 'checkbox-group':
  820. case 'radio-group':
  821. var type = attrs.type.replace('-group', '');
  822. attrs.values.reverse();
  823. for (i = attrs.values.length - 1; i >= 0; i--) {
  824. preview += '<div class="inner-checkbox"><input type="' + type + '" id="' + type + '-' + epoch + '-' + i + '" value="' + attrs.values[i].value + '" /><label for="' + type + '-' + epoch + '-' + i + '">' + attrs.values[i].label + '</label></div>';
  825. }
  826. break;
  827. case 'text':
  828. case 'password':
  829. case 'hidden':
  830. case 'email':
  831. case 'date':
  832. case 'checkbox':
  833. var toggle = attrs.toggle ? 'toggle': '';
  834. preview = '<input type="' + attrs.type + '" ' + toggle + ' placeholder="">';
  835. break;
  836. case 'autocomplete':
  837. preview = '<input class="ui-autocomplete-input" autocomplete="on" placeholder="">';
  838. break;
  839. default:
  840. preview = '<' + attrs.type + '></' + attrs.type + '>';
  841. }
  842. return preview;
  843. };
  844. // Select field html, since there may be multiple
  845. var selectFieldOptions = function selectFieldOptions(values, name, selected, multipleSelect) {
  846. var selectedType = multipleSelect ? 'checkbox': 'radio';
  847. if (typeof values !== 'object') {
  848. values = {
  849. label: '',
  850. value: ''
  851. };
  852. } else {
  853. values.label = values.label || '';
  854. values.value = values.value || '';
  855. }
  856. field = '<li>';
  857. field += '<input type="' + selectedType + '" ' + selected + ' class="select-option" name="' + name + '" />';
  858. field += '<input type="text" class="option-label" placeholder="' + opts.messages.optionLabelPlaceholder + '" value="' + values.label + '" />';
  859. field += '<input type="text" class="option-value" placeholder="' + opts.messages.optionValuePlaceholder + '" value="' + values.value + '" />';
  860. field += '<a href="#" class="remove btn" title="' + opts.messages.removeMessage + '">' + opts.messages.remove + '</a>';
  861. field += '</li>';
  862. return field;
  863. };
  864. // ---------------------- UTILITIES ---------------------- //
  865. // delete options
  866. $sortableFields.delegate('.remove', 'click',
  867. function(e) {
  868. e.preventDefault();
  869. var optionsCount = $(this).parents('.sortable-options:eq(0)').children('li').length;
  870. if (optionsCount <= 2) {
  871. alert('Error: ' + opts.messages.minOptionMessage);
  872. } else {
  873. $(this).parent('li').slideUp('250',
  874. function() {
  875. $(this).remove();
  876. });
  877. }
  878. });
  879. // toggle fields
  880. $sortableFields.on('click', '.toggle-form',
  881. function(e) {
  882. e.preventDefault();
  883. var targetID = $(this).attr('id');
  884. $(this).toggleClass('open').parent().next('.prev-holder').slideToggle(250);
  885. $(document.getElementById(targetID + '-fld')).slideToggle(250,
  886. function() {
  887. _helpers.save();
  888. });
  889. });
  890. // update preview to label
  891. $sortableFields.delegate('input[name="label"]', 'keyup',
  892. function() {
  893. $('.field-label', $(this).closest('li')).text($(this).val());
  894. });
  895. $sortableFields.delegate('input[name="introduce"]', 'keyup',
  896. function() {
  897. $('.field-introduce', $(this).closest('li')).text($(this).val());
  898. });
  899. $sortableFields.delegate('input[name="defaultVal"]', 'keyup',
  900. function() {
  901. _helpers.save();
  902. });
  903. // remove error styling when users tries to correct mistake
  904. $sortableFields.delegate('input.error', 'keyup',
  905. function() {
  906. $(this).removeClass('error');
  907. });
  908. // update preview for description
  909. $sortableFields.delegate('input[name="description"]', 'keyup',
  910. function() {
  911. var closestToolTip = $('.tooltip-element', $(this).closest('li'));
  912. if ($(this).val() !== '') {
  913. if (!closestToolTip.length) {
  914. var tt = '<span class="tooltip-element" tooltip="' + $(this).val() + '">?</span>';
  915. $('.toggle-form', $(this).closest('li')).before(tt);
  916. // _helpers.initTooltip(tt);
  917. } else {
  918. closestToolTip.attr('tooltip', $(this).val()).css('display', 'inline-block');
  919. }
  920. } else {
  921. if (closestToolTip.length) {
  922. closestToolTip.css('display', 'none');
  923. }
  924. }
  925. });
  926. _helpers.updateMultipleSelect();
  927. // format name attribute
  928. $sortableFields.delegate('input[name="name"]', 'keyup',
  929. function() {
  930. $(this).val(_helpers.safename($(this).val()));
  931. if ($(this).val() === '') {
  932. $(this).addClass('field_error').attr('placeholder', opts.messages.cannotBeEmpty);
  933. } else {
  934. $(this).removeClass('field_error');
  935. }
  936. });
  937. $sortableFields.delegate('input.fld-max-length', 'keyup',
  938. function() {
  939. $(this).val(_helpers.forceNumber($(this).val()));
  940. });
  941. // Delete field
  942. $sortableFields.delegate('.delete-confirm', 'click',
  943. function(e) {
  944. e.preventDefault();
  945. // lets see if the user really wants to remove this field... FOREVER
  946. var fieldWarnH3 = $('<h3/>').html('<span></span>' + opts.messages.warning),
  947. deleteID = $(this).attr('id').replace(/del_/, ''),
  948. delBtn = $(this),
  949. $field = $(document.getElementById('frm-' + deleteID + '-item')),
  950. toolTipPageX = delBtn.offset().left - $(window).scrollLeft(),
  951. toolTipPageY = delBtn.offset().top - $(window).scrollTop();
  952. if (opts.showWarning) {
  953. jQuery('<div />').append(fieldWarnH3, opts.messages.fieldRemoveWarning).dialog({
  954. modal: true,
  955. resizable: false,
  956. width: 300,
  957. dialogClass: 'ite-warning',
  958. open: function open() {
  959. $('.ui-widget-overlay').css({
  960. 'opacity': 0.0
  961. });
  962. },
  963. position: [toolTipPageX - 282, toolTipPageY - 178],
  964. buttons: [{
  965. text: opts.messages.yes,
  966. click: function click() {
  967. $field.slideUp(250,
  968. function() {
  969. $(this).remove();
  970. _helpers.save();
  971. });
  972. $(this).dialog('close');
  973. }
  974. },
  975. {
  976. text: opts.messages.no,
  977. 'class': 'cancel',
  978. click: function click() {
  979. $(this).dialog('close');
  980. }
  981. }]
  982. });
  983. } else {
  984. $field.slideUp(250,
  985. function() {
  986. $(this).remove();
  987. _helpers.save();
  988. });
  989. }
  990. if ($('.form-field', $sortableFields).length === 1) {
  991. $stageWrap.addClass('empty');
  992. }
  993. });
  994. // Attach a callback to toggle required asterisk
  995. $sortableFields.delegate('input.required', 'click',
  996. function() {
  997. var requiredAsterisk = $(this).parents('li.form-field').find('.required-asterisk');
  998. requiredAsterisk.toggle();
  999. });
  1000. // Attach a callback to toggle roles visibility
  1001. $sortableFields.delegate('input[name="enable_roles"]', 'click',
  1002. function() {
  1003. var roles = $(this).siblings('div.available-roles'),
  1004. enableRolesCB = $(this);
  1005. roles.slideToggle(250,
  1006. function() {
  1007. if (!enableRolesCB.is(':checked')) {
  1008. $('input[type="checkbox"]', roles).removeAttr('checked');
  1009. }
  1010. });
  1011. });
  1012. // Attach a callback to add new checkboxes
  1013. $sortableFields.delegate('.add_ck', 'click',
  1014. function() {
  1015. $(this).parent().before(selectFieldOptions());
  1016. return false;
  1017. });
  1018. $sortableFields.delegate('li.disabled .form-element', 'mouseenter',
  1019. function() {
  1020. _helpers.disabledTT($(this));
  1021. });
  1022. // Attach a callback to add new options
  1023. $sortableFields.delegate('.add_opt', 'click',
  1024. function(e) {
  1025. e.preventDefault();
  1026. var isMultiple = $(this).parents('.fields').first().find('input[name="multiple"]')[0].checked,
  1027. name = $(this).parents('.fields').find('.select-option:eq(0)').attr('name');
  1028. $(this).parents('.fields').first().find('.sortable-options').append(selectFieldOptions(false, name, false, isMultiple));
  1029. _helpers.updateMultipleSelect();
  1030. });
  1031. // Attach a callback to close link
  1032. $sortableFields.delegate('.close_field', 'click',
  1033. function(e) {
  1034. e.preventDefault();
  1035. $(this).parents('li.form-field').find('.toggle-form').trigger('click');
  1036. });
  1037. // Attach a callback to add new radio fields
  1038. $sortableFields.delegate('.add_rd', 'click',
  1039. function(e) {
  1040. e.preventDefault();
  1041. $(this).parent().before(selectFieldOptions(false, $(this).parents('.frm-holder').attr('id')));
  1042. });
  1043. $('.form-elements .fields .remove, .frmb .del-button').on('hover',
  1044. function() {
  1045. $(this).parents('li.form-field').toggleClass('delete');
  1046. });
  1047. // View XML
  1048. $(document.getElementById(frmbID + '-export-xml')).click(function(e) {
  1049. e.preventDefault();
  1050. var xml = elem.val(),
  1051. $pre = $('<pre />').text(xml);
  1052. $pre.dialog({
  1053. resizable: false,
  1054. modal: true,
  1055. width: 720,
  1056. dialogClass: 'frmb-xml',
  1057. overlay: {
  1058. color: '#333333'
  1059. }
  1060. });
  1061. });
  1062. // Clear all fields in form editor
  1063. $(document.getElementById(frmbID + '-clear-all')).click(function(e) {
  1064. e.preventDefault();
  1065. if (window.confirm(opts.messages.clearAllMessage)) {
  1066. $sortableFields.empty();
  1067. elem.val('');
  1068. _helpers.save();
  1069. var values = {
  1070. label: [opts.messages.descriptionField],
  1071. name: ['content'],
  1072. required: 'true',
  1073. description: opts.messages.mandatory
  1074. };
  1075. appendNewField(values);
  1076. $sortableFields.prepend(opts.disableFields.before);
  1077. $sortableFields.append(opts.disableFields.after);
  1078. }
  1079. });
  1080. // Save Idea Template
  1081. $(document.getElementById(frmbID + '-save')).click(function(e) {
  1082. if ($(this).find('.ldkInlineEdit').length === 0) {
  1083. e.preventDefault();
  1084. if (!$stageWrap.hasClass('edit-xml')) {
  1085. _helpers.save();
  1086. }
  1087. _helpers.validateForm(e);
  1088. }
  1089. });
  1090. var triggerDevMode = false,
  1091. keys = [],
  1092. devCode = '68,69,86';
  1093. // Super secret Developer Tools
  1094. $('.save.fb-button').mouseover(function() {
  1095. triggerDevMode = true;
  1096. }).mouseout(function() {
  1097. triggerDevMode = false;
  1098. });
  1099. $(document.documentElement).keydown(function(e) {
  1100. keys.push(e.keyCode);
  1101. if (keys.toString().indexOf(devCode) >= 0) {
  1102. $('.action-links').toggle();
  1103. $('.view-xml').toggle();
  1104. keys = [];
  1105. }
  1106. });
  1107. // Toggle Developer Mode
  1108. $('.dev-mode-link').click(function(e) {
  1109. e.preventDefault();
  1110. var dml = $(this);
  1111. $stageWrap.toggleClass('dev-mode');
  1112. dml.parent().css('opacity', 1);
  1113. if ($stageWrap.hasClass('dev-mode')) {
  1114. dml.siblings('.action-links-inner').css('width', '100%');
  1115. dml.html(opts.messages.devMode + ' ' + opts.messages.on).css('color', '#8CC63F');
  1116. } else {
  1117. dml.siblings('.action-links-inner').css('width', 0);
  1118. dml.html(opts.messages.devMode + ' ' + opts.messages.off).css('color', '#666666');
  1119. triggerDevMode = false;
  1120. $('.action-links').toggle();
  1121. $('.view-xml').toggle();
  1122. }
  1123. });
  1124. // Toggle Edit Names
  1125. $(document.getElementById(frmbID + '-edit-names')).click(function(e) {
  1126. e.preventDefault();
  1127. $(this).toggleClass('active');
  1128. $('.name-wrap', $sortableFields).slideToggle(250,
  1129. function() {
  1130. $stageWrap.toggleClass('edit-names');
  1131. });
  1132. });
  1133. // Toggle Allow Select
  1134. $(document.getElementById(frmbID + '-allow-select')).click(function(e) {
  1135. e.preventDefault();
  1136. $(this).toggleClass('active');
  1137. $('.allow-multi, .select-option', $sortableFields).slideToggle(250,
  1138. function() {
  1139. $stageWrap.toggleClass('allow-select');
  1140. });
  1141. });
  1142. // Toggle Edit XML
  1143. $(document.getElementById(frmbID + '-edit-xml')).click(function(e) {
  1144. e.preventDefault();
  1145. $(this).toggleClass('active');
  1146. $('textarea.idea-template').show();
  1147. $('.template-textarea-wrap').slideToggle(250);
  1148. $stageWrap.toggleClass('edit-xml');
  1149. });
  1150. elem.parent().find('p[id*="ideaTemplate"]').remove();
  1151. elem.wrap('<div class="template-textarea-wrap"/>');
  1152. elem.getTemplate();
  1153. };
  1154. $.fn.formBuilder = function(options) {
  1155. var form = this;
  1156. return form.each(function() {
  1157. var element = $(this);
  1158. if (element.data('formBuilder')) {
  1159. return;
  1160. }
  1161. var formBuilder = new FormBuilder(this, options);
  1162. element.data('formBuilder', formBuilder);
  1163. });
  1164. };
  1165. })(jQuery);
  1166. // toXML is a jQuery plugin that turns our form editor into XML
  1167. (function($) {
  1168. 'use strict';
  1169. $.fn.toXML = function(options) {
  1170. var defaults = {
  1171. prepend: '',
  1172. attributes: ['class']
  1173. };
  1174. var opts = $.extend(defaults, options);
  1175. var serialStr = '';
  1176. // Begin the core plugin
  1177. this.each(function() {
  1178. var liCount = 0;
  1179. var c = 1;
  1180. if ($(this).children().length >= 1) {
  1181. serialStr += '<form-template>\n\t<fields>';
  1182. // build new xml
  1183. $(this).children().each(function() {
  1184. var $field = $(this);
  1185. if (! ($field.hasClass('moving') || $field.hasClass('disabled'))) {
  1186. for (var att = 0; att < opts.attributes.length; att++) {
  1187. var required = $('input.required', $field).is(':checked') ? 'required="true" ': 'required="false" ',
  1188. multipleChecked = $('input[name="multiple"]', $field).is(':checked'),
  1189. multiple = multipleChecked ? 'style="multiple" ': '',
  1190. t = $field.attr(opts.attributes[att]).replace(' form-field', ''),
  1191. // field type
  1192. multipleField = t.match(/(select|checkbox-group|radio-group)/),
  1193. type = 'type="' + t + '" ',
  1194. fName = $('input.fld-name', $field).length > 0 ? ('name="' + $('input.fld-name', $field).val() + '" ') : '',
  1195. fLabel = 'label="' + $('input.fld-label', $field).val() + '" ',
  1196. roleVals = $.map($('input.roles-field:checked', $field),
  1197. function(n) {
  1198. return n.value;
  1199. }).join(','),
  1200. roles = roleVals !== '' ? 'role="' + roleVals + '" ': '',
  1201. desc = $('input.fld-description', $field).length > 0 ? ('description="' + $('input.fld-description', $field).val() + '" ') : '',
  1202. introduce = $('input.fld-introduce', $field).length > 0 ? ('introduce="' + $('input.fld-introduce', $field).val() + '" ') : '',
  1203. defaultVal = $('input.fld-defaultVal', $field).length > 0 ? ('defaultVal="' + $('input.fld-defaultVal', $field).val() + '" ') : '',
  1204. maxLengthVal = $('input.fld-max-length', $field).val(),
  1205. maxLength = 'max-length="' + (maxLengthVal !== undefined ? maxLengthVal: '') + '" ',
  1206. fSlash = !multipleField ? '/': '';
  1207. var fToggle = $('.checkbox-toggle', $field).is(':checked') ? 'toggle="true" ': '';
  1208. serialStr += '\n\t\t<field ' + fName + fLabel + defaultVal + fToggle + multiple + roles + desc + introduce + (maxLengthVal !== '' ? maxLengthVal !== undefined ? maxLength: '': '') + required + type + fSlash +'>';
  1209. if (multipleField) {
  1210. c = 1;
  1211. $('.sortable-options li', $field).each(function() {
  1212. var $option = $(this),
  1213. optionValue = 'value="' + $('.option-value', $option).val() + '"',
  1214. optionLabel = $('.option-label', $option).val(),
  1215. selected = $('.select-option', $option).is(':checked') ? ' selected="true"': '';
  1216. serialStr += '\n\t\t\t<option' + selected + ' ' + optionValue + '>' + optionLabel + '</option>';
  1217. c++;
  1218. });
  1219. serialStr += '\n\t\t</field>';
  1220. }
  1221. }
  1222. }
  1223. liCount++;
  1224. });
  1225. serialStr += '\n\t</fields>\n</form-template>';
  1226. } // if "$(this).children().length >= 1"
  1227. });
  1228. return serialStr;
  1229. };
  1230. })(jQuery);