~canonical-sysadmins/wordpress/4.8.2

« back to all changes in this revision

Viewing changes to wp-admin/js/image-edit.js

  • Committer: Barry Price
  • Date: 2017-06-09 02:09:58 UTC
  • mfrom: (1.1.26 upstream)
  • Revision ID: barry.price@canonical.com-20170609020958-838whhwt2196f2vk
Merge WP4.8 from upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* global imageEditL10n, ajaxurl, confirm */
 
2
/**
 
3
 * @summary   The functions necessary for editing images.
 
4
 *
 
5
 * @since     2.9.0
 
6
 */
2
7
 
3
8
(function($) {
4
 
var imageEdit = window.imageEdit = {
 
9
 
 
10
        /**
 
11
         * Contains all the methods to initialise and control the image editor.
 
12
         *
 
13
         * @namespace imageEdit
 
14
         */
 
15
        var imageEdit = window.imageEdit = {
5
16
        iasapi : {},
6
17
        hold : {},
7
18
        postid : '',
8
19
        _view : false,
9
20
 
 
21
        /**
 
22
         * @summary Converts a value to an integer.
 
23
         *
 
24
         * @memberof imageEdit
 
25
         * @since    2.9.0
 
26
         *
 
27
         * @param {number} f The float value that should be converted.
 
28
         *
 
29
         * @return {number} The integer representation from the float value.
 
30
         */
10
31
        intval : function(f) {
11
32
                /*
12
33
                 * Bitwise OR operator: one of the obscure ways to truncate floating point figures,
15
36
                return f | 0;
16
37
        },
17
38
 
 
39
        /**
 
40
         * @summary Adds the disabled attribute and class to a single form element
 
41
         *          or a field set.
 
42
         *
 
43
         * @memberof imageEdit
 
44
         * @since    2.9.0
 
45
         *
 
46
         * @param {jQuery}         el The element that should be modified.
 
47
         * @param {bool|number}    s  The state for the element. If set to true
 
48
         *                            the element is disabled,
 
49
         *                            otherwise the element is enabled.
 
50
         *                            The function is sometimes called with a 0 or 1
 
51
         *                            instead of true or false.
 
52
         *
 
53
         * @returns {void}
 
54
         */
18
55
        setDisabled : function( el, s ) {
19
56
                /*
20
57
                 * `el` can be a single form element or a fieldset. Before #28864, the disabled state on
29
66
                }
30
67
        },
31
68
 
 
69
        /**
 
70
         * @summary Initializes the image editor.
 
71
         *
 
72
         * @memberof imageEdit
 
73
         * @since    2.9.0
 
74
         *
 
75
         * @param {number} postid The post id.
 
76
         *
 
77
         * @returns {void}
 
78
         */
32
79
        init : function(postid) {
33
80
                var t = this, old = $('#image-editor-' + t.postid),
34
81
                        x = t.intval( $('#imgedit-x-' + postid).val() ),
48
95
                $('input[type="text"]', '#imgedit-panel-' + postid).keypress(function(e) {
49
96
                        var k = e.keyCode;
50
97
 
 
98
                        // Key codes 37 thru 40 are the arrow keys.
51
99
                        if ( 36 < k && k < 41 ) {
52
100
                                $(this).blur();
53
101
                        }
54
102
 
 
103
                        // The key code 13 is the enter key.
55
104
                        if ( 13 === k ) {
56
105
                                e.preventDefault();
57
106
                                e.stopPropagation();
60
109
                });
61
110
        },
62
111
 
 
112
        /**
 
113
         * @summary Toggles the wait/load icon in the editor.
 
114
         *
 
115
         * @memberof imageEdit
 
116
         * @since    2.9.0
 
117
         *
 
118
         * @param {number} postid The post id.
 
119
         * @param {number} toggle Is 0 or 1, fades the icon in then 1 and out when 0.
 
120
         *
 
121
         * @returns {void}
 
122
         */
63
123
        toggleEditor : function(postid, toggle) {
64
124
                var wait = $('#imgedit-wait-' + postid);
65
125
 
70
130
                }
71
131
        },
72
132
 
 
133
        /**
 
134
         * @summary Shows or hides the image edit help box.
 
135
         *
 
136
         * @memberof imageEdit
 
137
         * @since    2.9.0
 
138
         *
 
139
         * @param {HTMLElement} el The element to create the help window in.
 
140
         *
 
141
         * @returns {boolean} Always returns false.
 
142
         */
73
143
        toggleHelp : function(el) {
74
144
                var $el = $( el );
75
145
                $el
79
149
                return false;
80
150
        },
81
151
 
 
152
        /**
 
153
         * @summary Gets the value from the image edit target.
 
154
         *
 
155
         * The image edit target contains the image sizes where the (possible) changes
 
156
         * have to be applied to.
 
157
         *
 
158
         * @memberof imageEdit
 
159
         * @since    2.9.0
 
160
         *
 
161
         * @param {number} postid The post id.
 
162
         *
 
163
         * @returns {string} The value from the imagedit-save-target input field when available,
 
164
         *                   or 'full' when not available.
 
165
         */
82
166
        getTarget : function(postid) {
83
167
                return $('input[name="imgedit-target-' + postid + '"]:checked', '#imgedit-save-target-' + postid).val() || 'full';
84
168
        },
85
169
 
 
170
        /**
 
171
         * @summary Recalculates the height or width and keeps the original aspect ratio.
 
172
         *
 
173
         * If the original image size is exceeded a red exclamation mark is shown.
 
174
         *
 
175
         * @memberof imageEdit
 
176
         * @since    2.9.0
 
177
         *
 
178
         * @param {number}         postid The current post id.
 
179
         * @param {number}         x      Is 0 when it applies the y-axis
 
180
         *                                and 1 when applicable for the x-axis.
 
181
         * @param {jQuery}         el     Element.
 
182
         *
 
183
         * @returns {void}
 
184
         */
86
185
        scaleChanged : function( postid, x, el ) {
87
186
                var w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid),
88
187
                warn = $('#imgedit-scale-warn-' + postid), w1 = '', h1 = '';
106
205
                }
107
206
        },
108
207
 
 
208
        /**
 
209
         * @summary Gets the selected aspect ratio.
 
210
         *
 
211
         * @memberof imageEdit
 
212
         * @since    2.9.0
 
213
         *
 
214
         * @param {number} postid The post id.
 
215
         *
 
216
         * @returns {string} The aspect ratio.
 
217
         */
109
218
        getSelRatio : function(postid) {
110
219
                var x = this.hold.w, y = this.hold.h,
111
220
                        X = this.intval( $('#imgedit-crop-width-' + postid).val() ),
122
231
                return '1:1';
123
232
        },
124
233
 
 
234
        /**
 
235
         * @summary Removes the last action from the image edit history
 
236
         * The history consist of (edit) actions performed on the image.
 
237
         *
 
238
         * @memberof imageEdit
 
239
         * @since    2.9.0
 
240
         *
 
241
         * @param {number} postid  The post id.
 
242
         * @param {number} setSize 0 or 1, when 1 the image resets to its original size.
 
243
         *
 
244
         * @returns {string} JSON string containing the history or an empty string if no history exists.
 
245
         */
125
246
        filterHistory : function(postid, setSize) {
126
 
                // apply undo state to history
 
247
                // Apply undo state to history.
127
248
                var history = $('#imgedit-history-' + postid).val(), pop, n, o, i, op = [];
128
249
 
129
250
                if ( history !== '' ) {
 
251
                        // Read the JSON string with the image edit history.
130
252
                        history = JSON.parse(history);
131
253
                        pop = this.intval( $('#imgedit-undone-' + postid).val() );
132
254
                        if ( pop > 0 ) {
136
258
                                }
137
259
                        }
138
260
 
 
261
                        // Reset size to it's original state.
139
262
                        if ( setSize ) {
140
263
                                if ( !history.length ) {
141
264
                                        this.hold.w = this.hold.ow;
143
266
                                        return '';
144
267
                                }
145
268
 
146
 
                                // restore
 
269
                                // Restore original 'o'.
147
270
                                o = history[history.length - 1];
 
271
 
 
272
                                // c = 'crop', r = 'rotate', f = 'flip'
148
273
                                o = o.c || o.r || o.f || false;
149
274
 
150
275
                                if ( o ) {
 
276
                                        // fw = Full image width
151
277
                                        this.hold.w = o.fw;
 
278
                                        // fh = Full image height
152
279
                                        this.hold.h = o.fh;
153
280
                                }
154
281
                        }
155
282
 
156
 
                        // filter the values
 
283
                        // Filter the last step/action from the history.
157
284
                        for ( n in history ) {
158
285
                                i = history[n];
159
286
                                if ( i.hasOwnProperty('c') ) {
168
295
                }
169
296
                return '';
170
297
        },
171
 
 
 
298
        /**
 
299
         * @summary Binds the necessary events to the image.
 
300
         *
 
301
         * When the image source is reloaded the image will be reloaded.
 
302
         *
 
303
         * @memberof imageEdit
 
304
         * @since    2.9.0
 
305
         *
 
306
         * @param {number}   postid   The post id.
 
307
         * @param {string}   nonce    The nonce to verify the request.
 
308
         * @param {function} callback Function to execute when the image is loaded.
 
309
         *
 
310
         * @returns {void}
 
311
         */
172
312
        refreshEditor : function(postid, nonce, callback) {
173
313
                var t = this, data, img;
174
314
 
188
328
                                        t = imageEdit,
189
329
                                        historyObj;
190
330
 
 
331
                                // Checks if there already is some image-edit history.
191
332
                                if ( '' !== event.data.history ) {
192
333
                                        historyObj = JSON.parse( event.data.history );
193
334
                                        // If last executed action in history is a crop action.
230
371
                        })
231
372
                        .attr('src', ajaxurl + '?' + $.param(data));
232
373
        },
233
 
 
 
374
        /**
 
375
         * @summary Performs an image edit action.
 
376
         *
 
377
         * @memberof imageEdit
 
378
         * @since    2.9.0
 
379
         *
 
380
         * @param  {number}  postid The post id.
 
381
         * @param  {string}  nonce  The nonce to verify the request.
 
382
         * @param  {string}  action The action to perform on the image.
 
383
         *                          The possible actions are: "scale" and "restore".
 
384
         *
 
385
         * @returns {boolean|void} Executes a post request that refreshes the page
 
386
         *                         when the action is performed.
 
387
         *                         Returns false if a invalid action is given,
 
388
         *                         or when the action cannot be performed.
 
389
         */
234
390
        action : function(postid, nonce, action) {
235
391
                var t = this, data, w, h, fw, fh;
236
392
 
282
438
                });
283
439
        },
284
440
 
 
441
        /**
 
442
         * @summary Stores the changes that are made to the image.
 
443
         *
 
444
         * @memberof imageEdit
 
445
         * @since    2.9.0
 
446
         *
 
447
         * @param {number}  postid   The post id to get the image from the database.
 
448
         * @param {string}  nonce    The nonce to verify the request.
 
449
         *
 
450
         * @returns {boolean|void}  If the actions are successfully saved a response message is shown.
 
451
         *                          Returns false if there is no image editing history,
 
452
         *                          thus there are not edit-actions performed on the image.
 
453
         */
285
454
        save : function(postid, nonce) {
286
455
                var data,
287
456
                        target = this.getTarget(postid),
302
471
                        'context': $('#image-edit-context').length ? $('#image-edit-context').val() : null,
303
472
                        'do': 'save'
304
473
                };
305
 
 
 
474
                // Post the image edit data to the backend.
306
475
                $.post(ajaxurl, data, function(r) {
 
476
                        // Read the response.
307
477
                        var ret = JSON.parse(r);
308
478
 
 
479
                        // If a response is returned, close the editor and show an error.
309
480
                        if ( ret.error ) {
310
481
                                $('#imgedit-response-' + postid).html('<div class="error"><p>' + ret.error + '</p></div>');
311
482
                                imageEdit.close(postid);
332
503
                });
333
504
        },
334
505
 
 
506
        /**
 
507
         * @summary Creates the image edit window.
 
508
         *
 
509
         * @memberof imageEdit
 
510
         * @since    2.9.0
 
511
         *
 
512
         * @param {number} postid   The post id for the image.
 
513
         * @param {string} nonce    The nonce to verify the request.
 
514
         * @param {object} view     The image editor view to be used for the editing.
 
515
         *
 
516
         * @returns {void|promise} Either returns void if the button was already activated
 
517
         *                         or returns an instance of the image editor, wrapped in a promise.
 
518
         */
335
519
        open : function( postid, nonce, view ) {
336
520
                this._view = view;
337
521
 
376
560
                return dfd;
377
561
        },
378
562
 
 
563
        /**
 
564
         * @summary Initializes the cropping tool and sets a default cropping selection.
 
565
         *
 
566
         * @memberof imageEdit
 
567
         * @since    2.9.0
 
568
         *
 
569
         * @param {number} postid The post id.
 
570
         *
 
571
         * @returns {void}
 
572
         */
379
573
        imgLoaded : function(postid) {
380
574
                var img = $('#image-preview-' + postid), parent = $('#imgedit-crop-' + postid);
381
575
 
 
576
                // Ensure init has run even when directly loaded.
 
577
                if ( 'undefined' === typeof this.hold.sizer ) {
 
578
                        this.init( postid );
 
579
                }
 
580
 
382
581
                this.initCrop(postid, img, parent);
383
582
                this.setCropSelection(postid, 0);
384
583
                this.toggleEditor(postid, 0);
386
585
                $( '.imgedit-wrap .imgedit-help-toggle' ).eq( 0 ).focus();
387
586
        },
388
587
 
 
588
        /**
 
589
         * @summary Initializes the cropping tool.
 
590
         *
 
591
         * @memberof imageEdit
 
592
         * @since    2.9.0
 
593
         *
 
594
         * @param {number}      postid The post id.
 
595
         * @param {HTMLElement} image  The preview image.
 
596
         * @param {HTMLElement} parent The preview image container.
 
597
         *
 
598
         * @returns {void}
 
599
         */
389
600
        initCrop : function(postid, image, parent) {
390
601
                var t = this,
391
602
                        selW = $('#imgedit-sel-width-' + postid),
400
611
                        minWidth: 3,
401
612
                        minHeight: 3,
402
613
 
 
614
                        /**
 
615
                         * @summary Sets the CSS styles and binds events for locking the aspect ratio.
 
616
                         *
 
617
                         * @param {jQuery} img The preview image.
 
618
                         */
403
619
                        onInit: function( img ) {
404
 
                                // Ensure that the imgareaselect wrapper elements are position:absolute
 
620
                                // Ensure that the imgAreaSelect wrapper elements are position:absolute.
405
621
                                // (even if we're in a position:fixed modal)
406
622
                                $img = $( img );
407
623
                                $img.next().css( 'position', 'absolute' )
408
624
                                        .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' );
409
 
 
 
625
                                /**
 
626
                                 * @summary Binds mouse down event to the cropping container.
 
627
                                 *
 
628
                                 * @returns {void}
 
629
                                 */
410
630
                                parent.children().mousedown(function(e){
411
631
                                        var ratio = false, sel, defRatio;
412
632
 
422
642
                                });
423
643
                        },
424
644
 
 
645
                        /**
 
646
                         * @summary Event triggered when starting a selection.
 
647
                         *
 
648
                         * @returns {void}
 
649
                         */
425
650
                        onSelectStart: function() {
426
651
                                imageEdit.setDisabled($('#imgedit-crop-sel-' + postid), 1);
427
652
                        },
428
 
 
 
653
                        /**
 
654
                         * @summary Event triggered when the selection is ended.
 
655
                         *
 
656
                         * @param {object} img jQuery object representing the image.
 
657
                         * @param {object} c   The selection.
 
658
                         *
 
659
                         * @returns {object}
 
660
                         */
429
661
                        onSelectEnd: function(img, c) {
430
662
                                imageEdit.setCropSelection(postid, c);
431
663
                        },
432
664
 
 
665
                        /**
 
666
                         * @summary Event triggered when the selection changes.
 
667
                         *
 
668
                         * @param {object} img jQuery object representing the image.
 
669
                         * @param {object} c   The selection.
 
670
                         *
 
671
                         * @returns {void}
 
672
                         */
433
673
                        onSelectChange: function(img, c) {
434
674
                                var sizer = imageEdit.hold.sizer;
435
675
                                selW.val( imageEdit.round(c.width / sizer) );
438
678
                });
439
679
        },
440
680
 
 
681
        /**
 
682
         * @summary Stores the current crop selection.
 
683
         *
 
684
         * @memberof imageEdit
 
685
         * @since    2.9.0
 
686
         *
 
687
         * @param {number} postid The post id.
 
688
         * @param {object} c      The selection.
 
689
         *
 
690
         * @returns {boolean}
 
691
         */
441
692
        setCropSelection : function(postid, c) {
442
693
                var sel;
443
694
 
457
708
                $('#imgedit-selection-' + postid).val( JSON.stringify(sel) );
458
709
        },
459
710
 
 
711
 
 
712
        /**
 
713
         * @summary Closes the image editor.
 
714
         *
 
715
         * @memberof imageEdit
 
716
         * @since    2.9.0
 
717
         *
 
718
         * @param {number}  postid The post id.
 
719
         * @param {bool}    warn   Warning message.
 
720
         *
 
721
         * @returns {void|bool} Returns false if there is a warning.
 
722
         */
460
723
        close : function(postid, warn) {
461
724
                warn = warn || false;
462
725
 
487
750
 
488
751
        },
489
752
 
 
753
        /**
 
754
         * @summary Checks if the image edit history is saved.
 
755
         *
 
756
         * @memberof imageEdit
 
757
         * @since    2.9.0
 
758
         *
 
759
         * @param {number} postid The post id.
 
760
         *
 
761
         * @returns {boolean} Returns true if the history is not saved.
 
762
         */
490
763
        notsaved : function(postid) {
491
764
                var h = $('#imgedit-history-' + postid).val(),
492
765
                        history = ( h !== '' ) ? JSON.parse(h) : [],
501
774
                return false;
502
775
        },
503
776
 
 
777
        /**
 
778
         * @summary Adds an image edit action to the history.
 
779
         *
 
780
         * @memberof imageEdit
 
781
         * @since    2.9.0
 
782
         *
 
783
         * @param {object} op     The original position.
 
784
         * @param {number} postid The post id.
 
785
         * @param {string} nonce  The nonce.
 
786
         *
 
787
         * @returns {void}
 
788
         */
504
789
        addStep : function(op, postid, nonce) {
505
790
                var t = this, elem = $('#imgedit-history-' + postid),
506
791
                        history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [],
522
807
                });
523
808
        },
524
809
 
 
810
        /**
 
811
         * @summary Rotates the image.
 
812
         *
 
813
         * @memberof imageEdit
 
814
         * @since    2.9.0
 
815
         *
 
816
         * @param {string} angle  The angle the image is rotated with.
 
817
         * @param {number} postid The post id.
 
818
         * @param {string} nonce  The nonce.
 
819
         * @param {object} t      The target element.
 
820
         *
 
821
         * @returns {boolean}
 
822
         */
525
823
        rotate : function(angle, postid, nonce, t) {
526
824
                if ( $(t).hasClass('disabled') ) {
527
825
                        return false;
530
828
                this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce);
531
829
        },
532
830
 
 
831
        /**
 
832
         * @summary Flips the image.
 
833
         *
 
834
         * @memberof imageEdit
 
835
         * @since    2.9.0
 
836
         *
 
837
         * @param {number} axis   The axle the image is flipped on.
 
838
         * @param {number} postid The post id.
 
839
         * @param {string} nonce  The nonce.
 
840
         * @param {object} t      The target element.
 
841
         *
 
842
         * @returns {boolean}
 
843
         */
533
844
        flip : function (axis, postid, nonce, t) {
534
845
                if ( $(t).hasClass('disabled') ) {
535
846
                        return false;
538
849
                this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce);
539
850
        },
540
851
 
 
852
        /**
 
853
         * @summary Crops the image.
 
854
         *
 
855
         * @memberof imageEdit
 
856
         * @since    2.9.0
 
857
         *
 
858
         * @param {number} postid The post id.
 
859
         * @param {string} nonce  The nonce.
 
860
         * @param {object} t      The target object.
 
861
         *
 
862
         * @returns {void|boolean} Returns false if the crop button is disabled.
 
863
         */
541
864
        crop : function (postid, nonce, t) {
542
865
                var sel = $('#imgedit-selection-' + postid).val(),
543
866
                        w = this.intval( $('#imgedit-sel-width-' + postid).val() ),
555
878
                }
556
879
        },
557
880
 
 
881
        /**
 
882
         * @summary Undoes an image edit action.
 
883
         *
 
884
         * @memberof imageEdit
 
885
         * @since    2.9.0
 
886
         *
 
887
         * @param {number} postid   The post id.
 
888
         * @param {string} nonce    The nonce.
 
889
         *
 
890
         * @returns {void|false} Returns false if the undo button is disabled.
 
891
         */
558
892
        undo : function (postid, nonce) {
559
893
                var t = this, button = $('#image-undo-' + postid), elem = $('#imgedit-undone-' + postid),
560
894
                        pop = t.intval( elem.val() ) + 1;
577
911
                });
578
912
        },
579
913
 
 
914
        /**
 
915
         * Reverts a undo action.
 
916
         *
 
917
         * @memberof imageEdit
 
918
         * @since    2.9.0
 
919
         *
 
920
         * @param {number} postid The post id.
 
921
         * @param {string} nonce  The nonce.
 
922
         *
 
923
         * @returns {void}
 
924
         */
580
925
        redo : function(postid, nonce) {
581
926
                var t = this, button = $('#image-redo-' + postid), elem = $('#imgedit-undone-' + postid),
582
927
                        pop = t.intval( elem.val() ) - 1;
596
941
                });
597
942
        },
598
943
 
 
944
        /**
 
945
         * @summary Sets the selection for the height and width in pixels.
 
946
         *
 
947
         * @memberof imageEdit
 
948
         * @since    2.9.0
 
949
         *
 
950
         * @param {number} postid The post id.
 
951
         * @param {jQuery} el     The element containing the values.
 
952
         *
 
953
         * @returns {void|boolean} Returns false when the x or y value is lower than 1,
 
954
         *                         void when the value is not numeric or when the operation
 
955
         *                         is successful.
 
956
         */
599
957
        setNumSelection : function( postid, el ) {
600
958
                var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid),
601
959
                        x = this.intval( elX.val() ), y = this.intval( elY.val() ),
640
998
                }
641
999
        },
642
1000
 
 
1001
        /**
 
1002
         * Rounds a number to a whole.
 
1003
         *
 
1004
         * @memberof imageEdit
 
1005
         * @since    2.9.0
 
1006
         *
 
1007
         * @param {number} num The number.
 
1008
         *
 
1009
         * @returns {number} The number rounded to a whole number.
 
1010
         */
643
1011
        round : function(num) {
644
1012
                var s;
645
1013
                num = Math.round(num);
659
1027
                return num;
660
1028
        },
661
1029
 
 
1030
        /**
 
1031
         * Sets a locked aspect ratio for the selection.
 
1032
         *
 
1033
         * @memberof imageEdit
 
1034
         * @since    2.9.0
 
1035
         *
 
1036
         * @param {number} postid     The post id.
 
1037
         * @param {number} n          The ratio to set.
 
1038
         * @param {jQuery} el         The element containing the values.
 
1039
         *
 
1040
         * @returns {void}
 
1041
         */
662
1042
        setRatioSelection : function(postid, n, el) {
663
1043
                var sel, r, x = this.intval( $('#imgedit-crop-width-' + postid).val() ),
664
1044
                        y = this.intval( $('#imgedit-crop-height-' + postid).val() ),
691
1071
                }
692
1072
        },
693
1073
 
 
1074
        /**
 
1075
         * Validates if a value in a jQuery.HTMLElement is numeric.
 
1076
         *
 
1077
         * @memberof imageEdit
 
1078
         * @since    4.6
 
1079
         *
 
1080
         * @param {jQuery} el The html element.
 
1081
         *
 
1082
         * @returns {void|boolean} Returns false if the value is not numeric,
 
1083
         *                         void when it is.
 
1084
         */
694
1085
        validateNumeric: function( el ) {
695
1086
                if ( ! this.intval( $( el ).val() ) ) {
696
1087
                        $( el ).val( '' );