From a7a41d201db54d5962dd3f73948e81b60c583348 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 28 Jan 2015 23:12:16 +0100 Subject: [PATCH] Added central formDiff() method. Fixed clone() and added tests. --- .../_application_controller_form.js.coffee | 8 +- .../app/lib/app_post/utils.js.coffee | 57 +++- app/assets/javascripts/application.js | 54 +++- public/assets/tests/core.js | 57 ++++ public/assets/tests/form-validation.js | 3 +- public/assets/tests/form.js | 2 +- public/assets/tests/html-utils.js | 275 ++++++++++++++++++ 7 files changed, 446 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee index 95930afcc..0d086fea8 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee @@ -2324,7 +2324,9 @@ class App.ControllerForm extends App.Controller month = param[ "#{dateKey}month" ] day = param[ "#{dateKey}day" ] timezone = (new Date()).getTimezoneOffset()/60 - if year && month && day && day && !lookupForm.find('[data-name="' + namespace[0] + '"]').hasClass('is-hidden') + if lookupForm.find('[data-name="' + namespace[0] + '"]').hasClass('is-hidden') + param[ namespace[0] ] = null + else if year && month && day && day format = (number) -> if parseInt(number) < 10 number = "0#{number}" @@ -2360,7 +2362,9 @@ class App.ControllerForm extends App.Controller hour = param[ "#{datetimeKey}hour" ] minute = param[ "#{datetimeKey}minute" ] timezone = (new Date()).getTimezoneOffset()/60 - if year && month && day && hour && minute && !lookupForm.find('[data-name="' + namespace[0] + '"]').hasClass('is-hidden') + if lookupForm.find('[data-name="' + namespace[0] + '"]').hasClass('is-hidden') + param[ namespace[0] ] = null + else if year && month && day && hour && minute format = (number) -> if parseInt(number) < 10 number = "0#{number}" diff --git a/app/assets/javascripts/app/lib/app_post/utils.js.coffee b/app/assets/javascripts/app/lib/app_post/utils.js.coffee index bbdd552f5..4a5b37e07 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.js.coffee @@ -159,7 +159,7 @@ class App.Utils quote = (str) -> (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&") - console.log('SC', messageText, signatureText, quote(signatureText)) + #console.log('SC', messageText, signatureText, quote(signatureText)) regex = new RegExp( quote(signatureText), 'mi' ) if messageText.match(regex) false @@ -194,3 +194,58 @@ class App.Utils # cleanString = App.Utils.htmlAttributeCleanup( string ) @htmlAttributeCleanup: (string) -> string.replace(/((?![-a-zA-Z0-9_]+).|\n|\r|\t)/gm, '') + + # diff = App.Utils.formDiff( dataNow, dataLast ) + @formDiff: ( dataNowRaw, dataLastRaw ) -> + + # do type convertation to compare it against form + dataNow = clone(dataNowRaw) + @_formDiffNormalizer(dataNow) + dataLast = clone(dataLastRaw) + @_formDiffNormalizer(dataLast) + + @_formDiffChanges( dataNow, dataLast ) + + @_formDiffChanges: (dataNow, dataLast, changes = {}) -> + for dataNowkey, dataNowValue of dataNow + if dataNow[dataNowkey] isnt dataLast[dataNowkey] + if _.isArray( dataNow[dataNowkey] ) && _.isArray( dataLast[dataNowkey] ) + diff = _.difference( dataNow[dataNowkey], dataLast[dataNowkey] ) + if !_.isEmpty( diff ) + changes[dataNowkey] = diff + else if _.isObject( dataNow[dataNowkey] ) && _.isObject( dataLast[dataNowkey] ) + changes = @_formDiffChanges( dataNow[dataNowkey], dataLast[dataNowkey], changes ) + else + changes[dataNowkey] = dataNow[dataNowkey] + changes + + @_formDiffNormalizer: (data) -> + if _.isArray( data ) + for i in [0...data.length] + data[i] = @_formDiffNormalizer( data[i] ) + else if _.isObject( data ) + for key, value of data + + if _.isArray( data[key] ) + @_formDiffNormalizer( data[key] ) + else if _.isObject( data[key] ) + @_formDiffNormalizer( data[key] ) + else + data[key] = @_formDiffNormalizerItem( key, data[key] ) + else + @_formDiffNormalizerItem( '', data ) + + + @_formDiffNormalizerItem: (key, value) -> + + # handel owner/nobody behavior + if key is 'owner_id' && value.toString() is '1' + value = '' + else if typeof value is 'number' + value = value.toString() + + # handle null/undefined behavior - we just handle both as the same + else if value is null + value = undefined + + value diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 1fd527660..df73bc31a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -73,11 +73,55 @@ function difference(object1, object2) { return changes; } -function clone(object) { - if (!object) { - return object - } - return JSON.parse(JSON.stringify(object)); +// taken from http://stackoverflow.com/questions/4459928/how-to-deep-clone-in-javascript +function clone(item) { + if (!item) { return item; } // null, undefined values check + + var types = [ Number, String, Boolean ], + result; + + // normalizing primitives if someone did new String('aaa'), or new Number('444'); + types.forEach(function(type) { + if (item instanceof type) { + result = type( item ); + } + }); + + if (typeof result == "undefined") { + if (Object.prototype.toString.call( item ) === "[object Array]") { + result = []; + item.forEach(function(child, index, array) { + result[index] = clone( child ); + }); + } else if (typeof item == "object") { + // testing that this is DOM + if (item.nodeType && typeof item.cloneNode == "function") { + var result = item.cloneNode( true ); + } else if (!item.prototype) { // check that this is a literal + if (item instanceof Date) { + result = new Date(item); + } else { + // it is an object literal + result = {}; + for (var i in item) { + result[i] = clone( item[i] ); + } + } + } else { + // depending what you would like here, + // just keep the reference, or create new object + if (false && item.constructor) { + // would not advice to do that, reason? Read below + result = new item.constructor(); + } else { + result = item; + } + } + } else { + result = item; + } + } + return result; } jQuery.event.special.remove = { diff --git a/public/assets/tests/core.js b/public/assets/tests/core.js index 61d21bf67..505899f9b 100644 --- a/public/assets/tests/core.js +++ b/public/assets/tests/core.js @@ -426,6 +426,63 @@ test( "config", function() { }); }); + +// clone +test( "clone", function() { + + // simple + var tests = [ + { + key1: 123, + key2: 1234 + }, + { + key1: 123, + key2: '1234' + }, + [1,2,4,5,6], + { + key1: 123, + key2: null, + }, + { + key1: 123, + key2: undefined, + }, + { + key1: 123, + key2: undefined, + key3: { + keya: 'some', + keyb: undefined, + }, + key4: ['a', 'b', null, false, true, undefined], + }, + undefined, + false, + true, + null, + { + key1: 123, + key2: undefined, + key3: { + keya: 'some', + keyb: undefined, + }, + key4: ['a', 'b', { + a: 123, + b: [1,5,7,8,1213,1231321] + }], + }, + ]; + + _.each(tests, function(test) { + var item = clone( test ) + deepEqual( item, test, 'clone' ); + }); + +}); + // diff test( "diff", function() { diff --git a/public/assets/tests/form-validation.js b/public/assets/tests/form-validation.js index acdd55d7e..ff2a95be9 100644 --- a/public/assets/tests/form-validation.js +++ b/public/assets/tests/form-validation.js @@ -201,7 +201,8 @@ test( "date validation check", function() { App.ControllerForm.validate( { errors: errors, form: el } ) equal( el.find('[data-name="date1"]').closest('.form-group').hasClass('has-error'), true, 'check date1 has-error') - equal( el.find('[data-name="date1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check date1 error message') + equal( el.find('[data-name="date1"]').closest('.form-group').find('.help-inline').text(), '', 'check date1 error message') + //equal( el.find('[data-name="date1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check date1 error message') // set new values el.find('[name="{date}date1___day"]').val('1') diff --git a/public/assets/tests/form.js b/public/assets/tests/form.js index 12c4acec0..2532f2dcc 100644 --- a/public/assets/tests/form.js +++ b/public/assets/tests/form.js @@ -501,7 +501,7 @@ test( "form dependend fields check", function() { select2: "false", selectmulti2: [ "true", "false" ], selectmultioption1: "false", - datetime1: undefined, + datetime1: null, datetime2: undefined, datetime3: '2015-01-11T12:40:00.000Z', datetime4: undefined, diff --git a/public/assets/tests/html-utils.js b/public/assets/tests/html-utils.js index 5aed8c46e..f6f0a225f 100644 --- a/public/assets/tests/html-utils.js +++ b/public/assets/tests/html-utils.js @@ -604,4 +604,279 @@ test( "check attibute validation", function() { }); +// check form diff +test( "check form diff", function() { + + var dataNow = { + owner_id: 1, + pending_date: '2015-01-28T09:39:00Z', + } + var dataLast = { + owner_id: '', + pending_date: '2015-01-28T09:39:00Z', + } + var diff = {} + var result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: '1', + pending_date: '2015-01-28T09:39:00Z', + } + dataLast = { + owner_id: '', + pending_date: '2015-01-28T09:39:00Z', + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + pending_date: '2015-01-28T09:39:00Z', + } + dataLast = { + owner_id: 1, + pending_date: '2015-01-28T09:39:00Z', + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: '', + pending_date: '2015-01-28T09:39:00Z', + } + dataLast = { + pending_date: '2015-01-28T09:39:00Z', + } + diff = { + owner_id: '', + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: '', + state_ids: [1,5,6,7], + } + dataLast = {} + diff = { + owner_id: '', + state_ids: ['1','5','6','7'], + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: 1, + state_ids: [1,5,7,6], + } + dataLast = { + owner_id: '', + state_ids: [1,5,6,7], + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: 1, + state_ids: [1,5,6,7], + } + dataLast = { + state_ids: ['1','5','7'], + } + diff = { + owner_id: '', + state_ids: ['6'], + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: '', + state_ids: [1,5,6,7], + } + dataLast = { + owner_id: 1, + state_ids: [1,5,6,7], + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + owner_id: '', + state_ids: [1,5,6,7], + } + dataLast = { + owner_id: 5, + state_ids: [1,5,6,7], + } + diff = { + owner_id: '' + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + state_id: 4, + pending_time: '2015-01-28T11:34:00Z' + } + dataLast = { + state_id: 5, + pending_time: undefined + } + diff = { + state_id: '4', + pending_time: '2015-01-28T11:34:00Z' + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + pending_time: undefined + } + dataLast = { + pending_time: null + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + ticket: { + pending_time: undefined, + }, + } + dataLast = { + ticket: { + pending_time: null, + }, + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: '123', + ticket: { + pending_time: undefined, + }, + } + dataLast = { + test: '123', + ticket: { + pending_time: null, + }, + } + diff = {} + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: '123', + } + dataLast = {} + diff = { + test: '123', + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: '123', + } + dataLast = { + test: [1,2,3,4] + } + diff = { + test: '123', + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: '123', + } + dataLast = { + test: { + 1: 1, + 2: 2, + } + } + diff = { + test: '123', + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: [1,2,3,'4'] + } + dataLast = { + test: '123', + } + diff = { + test: ['1','2','3','4'] + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: { + 1: 1, + 2: 2, + } + } + dataLast = { + test: '123', + } + diff = { + test: { + 1: '1', + 2: '2', + } + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + + dataNow = { + test: '123', + ticket: { + pending_time: undefined, + }, + } + dataLast = { + ticket: { + pending_time: null, + }, + } + diff = { + test: '123', + } + result = App.Utils.formDiff( dataNow, dataLast ) + deepEqual( result, diff, 'check form diff' ); + + +}); + } \ No newline at end of file