Fossil SCM
Added a 'tick' mode to fossil.confirmer to more easily allow the triggering element to be visibly updated to reflect the countdown state. The editor's discard/reload button now visibly counts down from 3 if clicked.
Commit
3da4b94c44411de4632d0eb2dcfbff29c0d5e7d6ead85d3e134eece5ab898082
Parent
a84f6458c04800e…
2 files changed
+112
-21
+4
-2
+112
-21
| --- src/fossil.confirmer.js | ||
| +++ src/fossil.confirmer.js | ||
| @@ -23,19 +23,22 @@ | ||
| 23 | 23 | |
| 24 | 24 | Options: |
| 25 | 25 | |
| 26 | 26 | .initialText = initial text of the element. Defaults to the result |
| 27 | 27 | of the element's .value (for INPUT tags) or innerHTML (for |
| 28 | - everything else). | |
| 28 | + everything else). After the timeout/tick count expires, or if the | |
| 29 | + user confirms the operation, the element's text is re-set to this | |
| 30 | + value. | |
| 29 | 31 | |
| 30 | 32 | .confirmText = text to show when in "confirm mode". |
| 31 | 33 | Default=("Confirm: "+initialText), or something similar. |
| 32 | 34 | |
| 33 | 35 | .timeout = Number of milliseconds to wait for confirmation. |
| 34 | - Default=3000. | |
| 36 | + Default=3000. Alternately, use a combination of .ticks and | |
| 37 | + .ticktime. | |
| 35 | 38 | |
| 36 | - .onconfirm = function to call when clicked in confirm mode. Default | |
| 39 | + .onconfirm = function to call when clicked in confirm mode. Default | |
| 37 | 40 | = undefined. The function's "this" is the the DOM element to which |
| 38 | 41 | the countdown applies. |
| 39 | 42 | |
| 40 | 43 | .ontimeout = function to call when confirm is not issued. Default = |
| 41 | 44 | undefined. The function's "this" is the DOM element to which the |
| @@ -57,24 +60,60 @@ | ||
| 57 | 60 | .classWaiting = optional CSS class string (default='') which is |
| 58 | 61 | added to the target when it is waiting on a timeout. When the target |
| 59 | 62 | leaves timeout-wait mode, this class is removed. When timeout-wait |
| 60 | 63 | mode is entered, this class is added *before* the .onactivate |
| 61 | 64 | handler is called. |
| 65 | + | |
| 66 | + .ticktime = a number of ms to wait per tick (see the next item). | |
| 67 | + Default = 1000. | |
| 68 | + | |
| 69 | + .ticks = a number of "ticks" to wait, as an alternative to .timeout. | |
| 70 | + When this mode is active, the ontick callback will be triggered | |
| 71 | + immediately before each tick, including the first one. If both | |
| 72 | + .ticks and .timeout are set, only one will be used, but which one is | |
| 73 | + unspecified. If passed a ticks value with a truncated integer value | |
| 74 | + of 0 or less, it will throw an exception (e.g. that also applies if | |
| 75 | + it's passed 0.5). | |
| 76 | + | |
| 77 | + .ontick = when using .ticks, this callback is passed the current | |
| 78 | + tick number before each tick, and its "this" is the target | |
| 79 | + element. On each subsequent call, the tick count will be reduced by | |
| 80 | + 1, and it is passed 0 after the final tick expires or when the | |
| 81 | + action has been confirmed, immediately before the onconfirm or | |
| 82 | + ontimeout callback. The intention of the callback is to update the | |
| 83 | + label of the target element. If .ticks is set but .ontick is not | |
| 84 | + then a default implementation is used which updates the element with | |
| 85 | + the .confirmText, prepending a countdown to it. | |
| 62 | 86 | |
| 63 | 87 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 64 | 88 | console to track what it's doing. |
| 65 | 89 | |
| 66 | -Due to the nature of multi-threaded code, it is potentially possible | |
| 67 | -that confirmation and timeout actions BOTH happen if the user triggers | |
| 68 | -the associated action at "just the right millisecond" before the | |
| 69 | -timeout is triggered. | |
| 90 | +Various notes: | |
| 91 | + | |
| 92 | +- To change the default option values, modify the | |
| 93 | + fossil.confirmer.defaultOpts object. | |
| 94 | + | |
| 95 | +- Exceptions triggered via the callbacks are caught and emitted to the | |
| 96 | + dev console if the debug option is enabled, but are otherwise | |
| 97 | + ignored. | |
| 98 | + | |
| 99 | +- Due to the nature of multi-threaded code, it is potentially possible | |
| 100 | + that confirmation and timeout actions BOTH happen if the user | |
| 101 | + triggers the associated action at "just the right millisecond" | |
| 102 | + before the timeout is triggered. | |
| 70 | 103 | |
| 71 | -To change the default option values, modify the | |
| 72 | -fossil.confirmer.defaultOpts object. | |
| 104 | +TODO: add an invert option which activates if the timeout is reached | |
| 105 | +and "times out" if the element is clicked again. e.g. a button which | |
| 106 | +says "Saving..." and cancels the op if it's clicked again, else it | |
| 107 | +saves after X time/ticks. | |
| 73 | 108 | |
| 74 | 109 | Terse Change history: |
| 75 | 110 | |
| 111 | +- 20200507: | |
| 112 | + - Add a tick-based countdown in order to more easily support | |
| 113 | + updating the target element with the countdown. | |
| 114 | + | |
| 76 | 115 | - 20200506: |
| 77 | 116 | - Ported from jQuery to plain JS. |
| 78 | 117 | |
| 79 | 118 | - 20181112: |
| 80 | 119 | - extended to support certain INPUT elements. |
| @@ -90,56 +129,95 @@ | ||
| 90 | 129 | dbg("confirmer opt =",opt); |
| 91 | 130 | if(!f.Holder){ |
| 92 | 131 | f.isInput = (e)=>/^(input|textarea)$/i.test(e.nodeName); |
| 93 | 132 | f.Holder = function(target,opt){ |
| 94 | 133 | const self = this; |
| 95 | - self.target = target; | |
| 96 | - self.opt = opt; | |
| 97 | - self.timerID = undefined; | |
| 98 | - self.state = this.states.initial; | |
| 134 | + this.target = target; | |
| 135 | + this.opt = opt; | |
| 136 | + this.timerID = undefined; | |
| 137 | + this.state = this.states.initial; | |
| 99 | 138 | const isInput = f.isInput(target); |
| 100 | 139 | const updateText = function(msg){ |
| 101 | 140 | if(isInput) target.value = msg; |
| 102 | 141 | else target.innerHTML = msg; |
| 103 | 142 | } |
| 104 | - updateText(self.opt.initialText); | |
| 143 | + updateText(this.opt.initialText); | |
| 144 | + if(this.opt.ticks && !this.opt.ontick){ | |
| 145 | + this.opt.ontick = function(tick){ | |
| 146 | + updateText("("+tick+") "+self.opt.confirmText); | |
| 147 | + }; | |
| 148 | + } | |
| 105 | 149 | this.setClasses(false); |
| 106 | 150 | this.doTimeout = function() { |
| 107 | - this.timerID = undefined; | |
| 151 | + if(this.timerID){ | |
| 152 | + clearTimeout( this.timerID ); | |
| 153 | + delete this.timerID; | |
| 154 | + } | |
| 108 | 155 | if( this.state != this.states.waiting ) { |
| 109 | 156 | // it was already confirmed |
| 110 | 157 | return; |
| 111 | 158 | } |
| 112 | 159 | this.setClasses( false ); |
| 113 | 160 | this.state = this.states.initial; |
| 114 | 161 | dbg("Timeout triggered."); |
| 115 | - updateText(this.opt.initialText); | |
| 162 | + if( this.opt.ontick ){ | |
| 163 | + try{this.opt.ontick.call(this.target, 0)} | |
| 164 | + catch(e){dbg("ontick EXCEPTION:",e)} | |
| 165 | + } | |
| 116 | 166 | if( this.opt.ontimeout ) { |
| 117 | - this.opt.ontimeout.call(this.target); | |
| 167 | + try{this.opt.ontimeout.call(this.target)} | |
| 168 | + catch(e){dbg("ontimeout EXCEPTION:",e)} | |
| 118 | 169 | } |
| 170 | + updateText(this.opt.initialText); | |
| 119 | 171 | }; |
| 120 | 172 | target.addEventListener( |
| 121 | 173 | 'click', function(){ |
| 122 | 174 | switch( self.state ) { |
| 123 | 175 | case( self.states.waiting ): |
| 176 | + /* Cancel the wait on confirmation */ | |
| 124 | 177 | if( undefined !== self.timerID ){ |
| 125 | 178 | clearTimeout( self.timerID ); |
| 126 | 179 | delete self.timerID; |
| 127 | 180 | } |
| 128 | 181 | self.state = self.states.initial; |
| 129 | 182 | self.setClasses( false ); |
| 130 | 183 | dbg("Confirmed"); |
| 184 | + if( self.opt.ontick ){ | |
| 185 | + try{self.opt.ontick.call(self.target,0)} | |
| 186 | + catch(e){dbg("ontick EXCEPTION:",e)} | |
| 187 | + } | |
| 188 | + if( self.opt.onconfirm ){ | |
| 189 | + try{self.opt.onconfirm.call(self.target)} | |
| 190 | + catch(e){dbg("onconfirm EXCEPTION:",e)} | |
| 191 | + } | |
| 131 | 192 | updateText(self.opt.initialText); |
| 132 | - if( self.opt.onconfirm ) self.opt.onconfirm.call(self.target); | |
| 133 | 193 | break; |
| 134 | 194 | case( self.states.initial ): |
| 195 | + /* Enter the waiting-on-confirmation state... */ | |
| 196 | + if(self.opt.ticks) self.opt.currentTick = self.opt.ticks; | |
| 135 | 197 | self.setClasses( true ); |
| 136 | - if( self.opt.onactivate ) self.opt.onactivate.call( self.target ); | |
| 137 | 198 | self.state = self.states.waiting; |
| 138 | - dbg("Waiting "+self.opt.timeout+"ms on confirmation..."); | |
| 139 | 199 | updateText( self.opt.confirmText ); |
| 140 | - self.timerID = setTimeout(function(){self.doTimeout();},self.opt.timeout ); | |
| 200 | + if( self.opt.onactivate ) self.opt.onactivate.call( self.target ); | |
| 201 | + if( self.opt.ontick ) self.opt.ontick.call(self.target, self.opt.currentTick); | |
| 202 | + if(self.opt.timeout){ | |
| 203 | + dbg("Waiting "+self.opt.timeout+"ms on confirmation..."); | |
| 204 | + self.timerID = | |
| 205 | + setTimeout(()=>self.doTimeout(),self.opt.timeout ); | |
| 206 | + }else if(self.opt.ticks){ | |
| 207 | + dbg("Waiting on confirmation for "+self.opt.ticks | |
| 208 | + +" ticks of "+self.opt.ticktime+"ms each..."); | |
| 209 | + self.timerID = | |
| 210 | + setInterval(function(){ | |
| 211 | + if(0===--self.opt.currentTick) self.doTimeout(); | |
| 212 | + else{ | |
| 213 | + try{self.opt.ontick.call(self.target, | |
| 214 | + self.opt.currentTick)} | |
| 215 | + catch(e){dbg("ontick EXCEPTION:",e)} | |
| 216 | + } | |
| 217 | + },self.opt.ticktime); | |
| 218 | + } | |
| 141 | 219 | break; |
| 142 | 220 | default: // can't happen. |
| 143 | 221 | break; |
| 144 | 222 | } |
| 145 | 223 | }, false |
| @@ -172,10 +250,21 @@ | ||
| 172 | 250 | ) || "PLEASE SET .initialText" |
| 173 | 251 | },opt); |
| 174 | 252 | if(!opt.confirmText){ |
| 175 | 253 | opt.confirmText = "Confirm: "+opt.initialText; |
| 176 | 254 | } |
| 255 | + if(opt.ticks){ | |
| 256 | + delete opt.timeout; | |
| 257 | + opt.ticks = 0 | opt.ticks /* ensure it's an integer */; | |
| 258 | + if(opt.ticks<=0){ | |
| 259 | + throw new Error("ticks must be >0"); | |
| 260 | + } | |
| 261 | + if(opt.ticktime <= 0) opt.ticktime = 1000; | |
| 262 | + }else{ | |
| 263 | + delete opt.ontick; | |
| 264 | + delete opt.ticks; | |
| 265 | + } | |
| 177 | 266 | new f.Holder(elem,opt); |
| 178 | 267 | return this; |
| 179 | 268 | }; |
| 180 | 269 | /** |
| 181 | 270 | The default options for initConfirmer(). Tweak them to set the |
| @@ -183,14 +272,16 @@ | ||
| 183 | 272 | dynamically-generated, and can't reasonably be set in the |
| 184 | 273 | defaults. |
| 185 | 274 | */ |
| 186 | 275 | F.confirmer.defaultOpts = { |
| 187 | 276 | timeout:3000, |
| 277 | + ticks: undefined, | |
| 278 | + ticktime: 998/*not *quite* 1000*/, | |
| 188 | 279 | onconfirm: undefined, |
| 189 | 280 | ontimeout: undefined, |
| 190 | 281 | onactivate: undefined, |
| 191 | 282 | classInitial: '', |
| 192 | 283 | classWaiting: '', |
| 193 | 284 | debug: false |
| 194 | 285 | }; |
| 195 | 286 | |
| 196 | 287 | })(window.fossil); |
| 197 | 288 |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -23,19 +23,22 @@ | |
| 23 | |
| 24 | Options: |
| 25 | |
| 26 | .initialText = initial text of the element. Defaults to the result |
| 27 | of the element's .value (for INPUT tags) or innerHTML (for |
| 28 | everything else). |
| 29 | |
| 30 | .confirmText = text to show when in "confirm mode". |
| 31 | Default=("Confirm: "+initialText), or something similar. |
| 32 | |
| 33 | .timeout = Number of milliseconds to wait for confirmation. |
| 34 | Default=3000. |
| 35 | |
| 36 | .onconfirm = function to call when clicked in confirm mode. Default |
| 37 | = undefined. The function's "this" is the the DOM element to which |
| 38 | the countdown applies. |
| 39 | |
| 40 | .ontimeout = function to call when confirm is not issued. Default = |
| 41 | undefined. The function's "this" is the DOM element to which the |
| @@ -57,24 +60,60 @@ | |
| 57 | .classWaiting = optional CSS class string (default='') which is |
| 58 | added to the target when it is waiting on a timeout. When the target |
| 59 | leaves timeout-wait mode, this class is removed. When timeout-wait |
| 60 | mode is entered, this class is added *before* the .onactivate |
| 61 | handler is called. |
| 62 | |
| 63 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 64 | console to track what it's doing. |
| 65 | |
| 66 | Due to the nature of multi-threaded code, it is potentially possible |
| 67 | that confirmation and timeout actions BOTH happen if the user triggers |
| 68 | the associated action at "just the right millisecond" before the |
| 69 | timeout is triggered. |
| 70 | |
| 71 | To change the default option values, modify the |
| 72 | fossil.confirmer.defaultOpts object. |
| 73 | |
| 74 | Terse Change history: |
| 75 | |
| 76 | - 20200506: |
| 77 | - Ported from jQuery to plain JS. |
| 78 | |
| 79 | - 20181112: |
| 80 | - extended to support certain INPUT elements. |
| @@ -90,56 +129,95 @@ | |
| 90 | dbg("confirmer opt =",opt); |
| 91 | if(!f.Holder){ |
| 92 | f.isInput = (e)=>/^(input|textarea)$/i.test(e.nodeName); |
| 93 | f.Holder = function(target,opt){ |
| 94 | const self = this; |
| 95 | self.target = target; |
| 96 | self.opt = opt; |
| 97 | self.timerID = undefined; |
| 98 | self.state = this.states.initial; |
| 99 | const isInput = f.isInput(target); |
| 100 | const updateText = function(msg){ |
| 101 | if(isInput) target.value = msg; |
| 102 | else target.innerHTML = msg; |
| 103 | } |
| 104 | updateText(self.opt.initialText); |
| 105 | this.setClasses(false); |
| 106 | this.doTimeout = function() { |
| 107 | this.timerID = undefined; |
| 108 | if( this.state != this.states.waiting ) { |
| 109 | // it was already confirmed |
| 110 | return; |
| 111 | } |
| 112 | this.setClasses( false ); |
| 113 | this.state = this.states.initial; |
| 114 | dbg("Timeout triggered."); |
| 115 | updateText(this.opt.initialText); |
| 116 | if( this.opt.ontimeout ) { |
| 117 | this.opt.ontimeout.call(this.target); |
| 118 | } |
| 119 | }; |
| 120 | target.addEventListener( |
| 121 | 'click', function(){ |
| 122 | switch( self.state ) { |
| 123 | case( self.states.waiting ): |
| 124 | if( undefined !== self.timerID ){ |
| 125 | clearTimeout( self.timerID ); |
| 126 | delete self.timerID; |
| 127 | } |
| 128 | self.state = self.states.initial; |
| 129 | self.setClasses( false ); |
| 130 | dbg("Confirmed"); |
| 131 | updateText(self.opt.initialText); |
| 132 | if( self.opt.onconfirm ) self.opt.onconfirm.call(self.target); |
| 133 | break; |
| 134 | case( self.states.initial ): |
| 135 | self.setClasses( true ); |
| 136 | if( self.opt.onactivate ) self.opt.onactivate.call( self.target ); |
| 137 | self.state = self.states.waiting; |
| 138 | dbg("Waiting "+self.opt.timeout+"ms on confirmation..."); |
| 139 | updateText( self.opt.confirmText ); |
| 140 | self.timerID = setTimeout(function(){self.doTimeout();},self.opt.timeout ); |
| 141 | break; |
| 142 | default: // can't happen. |
| 143 | break; |
| 144 | } |
| 145 | }, false |
| @@ -172,10 +250,21 @@ | |
| 172 | ) || "PLEASE SET .initialText" |
| 173 | },opt); |
| 174 | if(!opt.confirmText){ |
| 175 | opt.confirmText = "Confirm: "+opt.initialText; |
| 176 | } |
| 177 | new f.Holder(elem,opt); |
| 178 | return this; |
| 179 | }; |
| 180 | /** |
| 181 | The default options for initConfirmer(). Tweak them to set the |
| @@ -183,14 +272,16 @@ | |
| 183 | dynamically-generated, and can't reasonably be set in the |
| 184 | defaults. |
| 185 | */ |
| 186 | F.confirmer.defaultOpts = { |
| 187 | timeout:3000, |
| 188 | onconfirm: undefined, |
| 189 | ontimeout: undefined, |
| 190 | onactivate: undefined, |
| 191 | classInitial: '', |
| 192 | classWaiting: '', |
| 193 | debug: false |
| 194 | }; |
| 195 | |
| 196 | })(window.fossil); |
| 197 |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -23,19 +23,22 @@ | |
| 23 | |
| 24 | Options: |
| 25 | |
| 26 | .initialText = initial text of the element. Defaults to the result |
| 27 | of the element's .value (for INPUT tags) or innerHTML (for |
| 28 | everything else). After the timeout/tick count expires, or if the |
| 29 | user confirms the operation, the element's text is re-set to this |
| 30 | value. |
| 31 | |
| 32 | .confirmText = text to show when in "confirm mode". |
| 33 | Default=("Confirm: "+initialText), or something similar. |
| 34 | |
| 35 | .timeout = Number of milliseconds to wait for confirmation. |
| 36 | Default=3000. Alternately, use a combination of .ticks and |
| 37 | .ticktime. |
| 38 | |
| 39 | .onconfirm = function to call when clicked in confirm mode. Default |
| 40 | = undefined. The function's "this" is the the DOM element to which |
| 41 | the countdown applies. |
| 42 | |
| 43 | .ontimeout = function to call when confirm is not issued. Default = |
| 44 | undefined. The function's "this" is the DOM element to which the |
| @@ -57,24 +60,60 @@ | |
| 60 | .classWaiting = optional CSS class string (default='') which is |
| 61 | added to the target when it is waiting on a timeout. When the target |
| 62 | leaves timeout-wait mode, this class is removed. When timeout-wait |
| 63 | mode is entered, this class is added *before* the .onactivate |
| 64 | handler is called. |
| 65 | |
| 66 | .ticktime = a number of ms to wait per tick (see the next item). |
| 67 | Default = 1000. |
| 68 | |
| 69 | .ticks = a number of "ticks" to wait, as an alternative to .timeout. |
| 70 | When this mode is active, the ontick callback will be triggered |
| 71 | immediately before each tick, including the first one. If both |
| 72 | .ticks and .timeout are set, only one will be used, but which one is |
| 73 | unspecified. If passed a ticks value with a truncated integer value |
| 74 | of 0 or less, it will throw an exception (e.g. that also applies if |
| 75 | it's passed 0.5). |
| 76 | |
| 77 | .ontick = when using .ticks, this callback is passed the current |
| 78 | tick number before each tick, and its "this" is the target |
| 79 | element. On each subsequent call, the tick count will be reduced by |
| 80 | 1, and it is passed 0 after the final tick expires or when the |
| 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | then a default implementation is used which updates the element with |
| 85 | the .confirmText, prepending a countdown to it. |
| 86 | |
| 87 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 88 | console to track what it's doing. |
| 89 | |
| 90 | Various notes: |
| 91 | |
| 92 | - To change the default option values, modify the |
| 93 | fossil.confirmer.defaultOpts object. |
| 94 | |
| 95 | - Exceptions triggered via the callbacks are caught and emitted to the |
| 96 | dev console if the debug option is enabled, but are otherwise |
| 97 | ignored. |
| 98 | |
| 99 | - Due to the nature of multi-threaded code, it is potentially possible |
| 100 | that confirmation and timeout actions BOTH happen if the user |
| 101 | triggers the associated action at "just the right millisecond" |
| 102 | before the timeout is triggered. |
| 103 | |
| 104 | TODO: add an invert option which activates if the timeout is reached |
| 105 | and "times out" if the element is clicked again. e.g. a button which |
| 106 | says "Saving..." and cancels the op if it's clicked again, else it |
| 107 | saves after X time/ticks. |
| 108 | |
| 109 | Terse Change history: |
| 110 | |
| 111 | - 20200507: |
| 112 | - Add a tick-based countdown in order to more easily support |
| 113 | updating the target element with the countdown. |
| 114 | |
| 115 | - 20200506: |
| 116 | - Ported from jQuery to plain JS. |
| 117 | |
| 118 | - 20181112: |
| 119 | - extended to support certain INPUT elements. |
| @@ -90,56 +129,95 @@ | |
| 129 | dbg("confirmer opt =",opt); |
| 130 | if(!f.Holder){ |
| 131 | f.isInput = (e)=>/^(input|textarea)$/i.test(e.nodeName); |
| 132 | f.Holder = function(target,opt){ |
| 133 | const self = this; |
| 134 | this.target = target; |
| 135 | this.opt = opt; |
| 136 | this.timerID = undefined; |
| 137 | this.state = this.states.initial; |
| 138 | const isInput = f.isInput(target); |
| 139 | const updateText = function(msg){ |
| 140 | if(isInput) target.value = msg; |
| 141 | else target.innerHTML = msg; |
| 142 | } |
| 143 | updateText(this.opt.initialText); |
| 144 | if(this.opt.ticks && !this.opt.ontick){ |
| 145 | this.opt.ontick = function(tick){ |
| 146 | updateText("("+tick+") "+self.opt.confirmText); |
| 147 | }; |
| 148 | } |
| 149 | this.setClasses(false); |
| 150 | this.doTimeout = function() { |
| 151 | if(this.timerID){ |
| 152 | clearTimeout( this.timerID ); |
| 153 | delete this.timerID; |
| 154 | } |
| 155 | if( this.state != this.states.waiting ) { |
| 156 | // it was already confirmed |
| 157 | return; |
| 158 | } |
| 159 | this.setClasses( false ); |
| 160 | this.state = this.states.initial; |
| 161 | dbg("Timeout triggered."); |
| 162 | if( this.opt.ontick ){ |
| 163 | try{this.opt.ontick.call(this.target, 0)} |
| 164 | catch(e){dbg("ontick EXCEPTION:",e)} |
| 165 | } |
| 166 | if( this.opt.ontimeout ) { |
| 167 | try{this.opt.ontimeout.call(this.target)} |
| 168 | catch(e){dbg("ontimeout EXCEPTION:",e)} |
| 169 | } |
| 170 | updateText(this.opt.initialText); |
| 171 | }; |
| 172 | target.addEventListener( |
| 173 | 'click', function(){ |
| 174 | switch( self.state ) { |
| 175 | case( self.states.waiting ): |
| 176 | /* Cancel the wait on confirmation */ |
| 177 | if( undefined !== self.timerID ){ |
| 178 | clearTimeout( self.timerID ); |
| 179 | delete self.timerID; |
| 180 | } |
| 181 | self.state = self.states.initial; |
| 182 | self.setClasses( false ); |
| 183 | dbg("Confirmed"); |
| 184 | if( self.opt.ontick ){ |
| 185 | try{self.opt.ontick.call(self.target,0)} |
| 186 | catch(e){dbg("ontick EXCEPTION:",e)} |
| 187 | } |
| 188 | if( self.opt.onconfirm ){ |
| 189 | try{self.opt.onconfirm.call(self.target)} |
| 190 | catch(e){dbg("onconfirm EXCEPTION:",e)} |
| 191 | } |
| 192 | updateText(self.opt.initialText); |
| 193 | break; |
| 194 | case( self.states.initial ): |
| 195 | /* Enter the waiting-on-confirmation state... */ |
| 196 | if(self.opt.ticks) self.opt.currentTick = self.opt.ticks; |
| 197 | self.setClasses( true ); |
| 198 | self.state = self.states.waiting; |
| 199 | updateText( self.opt.confirmText ); |
| 200 | if( self.opt.onactivate ) self.opt.onactivate.call( self.target ); |
| 201 | if( self.opt.ontick ) self.opt.ontick.call(self.target, self.opt.currentTick); |
| 202 | if(self.opt.timeout){ |
| 203 | dbg("Waiting "+self.opt.timeout+"ms on confirmation..."); |
| 204 | self.timerID = |
| 205 | setTimeout(()=>self.doTimeout(),self.opt.timeout ); |
| 206 | }else if(self.opt.ticks){ |
| 207 | dbg("Waiting on confirmation for "+self.opt.ticks |
| 208 | +" ticks of "+self.opt.ticktime+"ms each..."); |
| 209 | self.timerID = |
| 210 | setInterval(function(){ |
| 211 | if(0===--self.opt.currentTick) self.doTimeout(); |
| 212 | else{ |
| 213 | try{self.opt.ontick.call(self.target, |
| 214 | self.opt.currentTick)} |
| 215 | catch(e){dbg("ontick EXCEPTION:",e)} |
| 216 | } |
| 217 | },self.opt.ticktime); |
| 218 | } |
| 219 | break; |
| 220 | default: // can't happen. |
| 221 | break; |
| 222 | } |
| 223 | }, false |
| @@ -172,10 +250,21 @@ | |
| 250 | ) || "PLEASE SET .initialText" |
| 251 | },opt); |
| 252 | if(!opt.confirmText){ |
| 253 | opt.confirmText = "Confirm: "+opt.initialText; |
| 254 | } |
| 255 | if(opt.ticks){ |
| 256 | delete opt.timeout; |
| 257 | opt.ticks = 0 | opt.ticks /* ensure it's an integer */; |
| 258 | if(opt.ticks<=0){ |
| 259 | throw new Error("ticks must be >0"); |
| 260 | } |
| 261 | if(opt.ticktime <= 0) opt.ticktime = 1000; |
| 262 | }else{ |
| 263 | delete opt.ontick; |
| 264 | delete opt.ticks; |
| 265 | } |
| 266 | new f.Holder(elem,opt); |
| 267 | return this; |
| 268 | }; |
| 269 | /** |
| 270 | The default options for initConfirmer(). Tweak them to set the |
| @@ -183,14 +272,16 @@ | |
| 272 | dynamically-generated, and can't reasonably be set in the |
| 273 | defaults. |
| 274 | */ |
| 275 | F.confirmer.defaultOpts = { |
| 276 | timeout:3000, |
| 277 | ticks: undefined, |
| 278 | ticktime: 998/*not *quite* 1000*/, |
| 279 | onconfirm: undefined, |
| 280 | ontimeout: undefined, |
| 281 | onactivate: undefined, |
| 282 | classInitial: '', |
| 283 | classWaiting: '', |
| 284 | debug: false |
| 285 | }; |
| 286 | |
| 287 | })(window.fossil); |
| 288 |
+4
-2
| --- src/fossil.page.fileedit.js | ||
| +++ src/fossil.page.fileedit.js | ||
| @@ -54,13 +54,15 @@ | ||
| 54 | 54 | ); |
| 55 | 55 | P.e.btnCommit.addEventListener( |
| 56 | 56 | "click",(e)=>P.commit(), false |
| 57 | 57 | ); |
| 58 | 58 | if(P.e.btnReload){ |
| 59 | + const label = "Really reload, losing edits?"; | |
| 59 | 60 | F.confirmer(P.e.btnReload, { |
| 60 | - confirmText: "Really reload, losing edits?", | |
| 61 | - onconfirm: (e)=>P.loadFile() | |
| 61 | + confirmText: label, | |
| 62 | + onconfirm: (e)=>P.loadFile(), | |
| 63 | + ticks: 3 | |
| 62 | 64 | }); |
| 63 | 65 | } |
| 64 | 66 | /** |
| 65 | 67 | Cosmetic: jump through some hoops to enable/disable |
| 66 | 68 | certain preview options depending on the current |
| 67 | 69 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -54,13 +54,15 @@ | |
| 54 | ); |
| 55 | P.e.btnCommit.addEventListener( |
| 56 | "click",(e)=>P.commit(), false |
| 57 | ); |
| 58 | if(P.e.btnReload){ |
| 59 | F.confirmer(P.e.btnReload, { |
| 60 | confirmText: "Really reload, losing edits?", |
| 61 | onconfirm: (e)=>P.loadFile() |
| 62 | }); |
| 63 | } |
| 64 | /** |
| 65 | Cosmetic: jump through some hoops to enable/disable |
| 66 | certain preview options depending on the current |
| 67 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -54,13 +54,15 @@ | |
| 54 | ); |
| 55 | P.e.btnCommit.addEventListener( |
| 56 | "click",(e)=>P.commit(), false |
| 57 | ); |
| 58 | if(P.e.btnReload){ |
| 59 | const label = "Really reload, losing edits?"; |
| 60 | F.confirmer(P.e.btnReload, { |
| 61 | confirmText: label, |
| 62 | onconfirm: (e)=>P.loadFile(), |
| 63 | ticks: 3 |
| 64 | }); |
| 65 | } |
| 66 | /** |
| 67 | Cosmetic: jump through some hoops to enable/disable |
| 68 | certain preview options depending on the current |
| 69 |