Fossil

Check-in [e70ab3a368]
Login

Check-in [e70ab3a368]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Added fossil.confirmer JS API to offer a non-intrusive click confirmation mechanism. Re-activated the /filepage content reload button with a confirmation click required.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | fileedit-ajaxify
Files: files | file ages | folders
SHA3-256: e70ab3a368b78adfb76feb58e86e647cd92e3428ffc12fb0c47e2860bafed110
User & Date: stephan 2020-05-06 03:02:23.302
Context
2020-05-06
03:19
Removed devious hard tabs which slipped in while porting. ... (check-in: 626c45609e user: stephan tags: fileedit-ajaxify)
03:02
Added fossil.confirmer JS API to offer a non-intrusive click confirmation mechanism. Re-activated the /filepage content reload button with a confirmation click required. ... (check-in: e70ab3a368 user: stephan tags: fileedit-ajaxify)
01:58
Removed the FORM element - it was superfluous and particularly stubborn in how it responded to every button. ... (check-in: 7635d9345d user: stephan tags: fileedit-ajaxify)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/fileedit.c.
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575




1576
1577
1578
1579
1580
1581
1582
1583
  /******* Content tab *******/
  {
    CX("<div id='fileedit-tab-content' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Content'"
       ">");
    CX("<div class='fileedit-options flex-container row'>");
    if(0){
      /* Discard/reload button. Leave this out until we have a
      ** nice way of offering confirmation, e.g. like the old
      ** jQuery.confirmer plugin which required a 2nd click of the
      ** button within X seconds to confirm. Right now it's simply
      ** to easy to tap by accident. */
      CX("<button class='fileedit-content-reload'>Discard &amp; Reload"




         "</button>");
    }
    style_select_list_int("select-font-size",
                          "editor_font_size", "Editor Font Size",
                          NULL/*tooltip*/,
                          100,
                          "100%", 100, "125%", 125,
                          "150%", 150, "175%", 175,







|





|
>
>
>
>
|







1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
  /******* Content tab *******/
  {
    CX("<div id='fileedit-tab-content' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Content'"
       ">");
    CX("<div class='fileedit-options flex-container row'>");
    if(1){
      /* Discard/reload button. Leave this out until we have a
      ** nice way of offering confirmation, e.g. like the old
      ** jQuery.confirmer plugin which required a 2nd click of the
      ** button within X seconds to confirm. Right now it's simply
      ** to easy to tap by accident. */
      CX("<button class='fileedit-content-reload confirmer' "
         "title='Reload the file from the server, discarding "
         "any local edits. To help avoid accidental loss of "
         "edits, it requires confirmation (a second click) within "
         "a few seconds or it will not reload.'"
         ">Discard &amp; Reload</button>");
    }
    style_select_list_int("select-font-size",
                          "editor_font_size", "Editor Font Size",
                          NULL/*tooltip*/,
                          100,
                          "100%", 100, "125%", 125,
                          "150%", 150, "175%", 175,
1772
1773
1774
1775
1776
1777
1778

1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
  }
  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);
  style_emit_script_fossil_bootstrap(0);
  style_emit_script_fetch(0);
  style_emit_script_tabs(0);
  style_emit_script_builtin("fossil.page.fileedit.js",0);

  if(blob_size(&endScript)>0){
    style_emit_script_tag(0,0);
    CX("(function(){\n");
    CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
       &endScript);
    CX("})();");
    style_emit_script_tag(1,0);
  }
  db_end_transaction(0);
  style_footer();
}







>











1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
  }
  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);
  style_emit_script_fossil_bootstrap(0);
  style_emit_script_fetch(0);
  style_emit_script_tabs(0);
  style_emit_script_builtin("fossil.page.fileedit.js",0);
  style_emit_script_confirmer(0);
  if(blob_size(&endScript)>0){
    style_emit_script_tag(0,0);
    CX("(function(){\n");
    CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
       &endScript);
    CX("})();");
    style_emit_script_tag(1,0);
  }
  db_end_transaction(0);
  style_footer();
}
Changes to src/fossil.bootstrap.js.
1
2
3
4
5
6
7


8
9
10
11
12
13
14
"use strict";
(function(global){
  /* Bootstrapping bits for the global.fossil object. Must be
     loaded after style.c:style_emit_script_tag() has initialized
     that object.
  */



  /**
     Returns the current time in something approximating
     ISO-8601 format.
  */
  const timestring = function f(){
    if(!f.rx1){
      f.rx1 = /\.\d+Z$/;







>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"use strict";
(function(global){
  /* Bootstrapping bits for the global.fossil object. Must be
     loaded after style.c:style_emit_script_tag() has initialized
     that object.
  */

  const F = global.fossil;

  /**
     Returns the current time in something approximating
     ISO-8601 format.
  */
  const timestring = function f(){
    if(!f.rx1){
      f.rx1 = /\.\d+Z$/;
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  ** element, its innerText gets assigned to the concatenation of all
  ** arguments (with a space between each), and the CSS 'error' class is
  ** removed from the object. Pass it a falsy value to clear the target
  ** element.
  **
  ** Returns this object.
  */
  global.fossil.message = function f(msg){
    const args = Array.prototype.slice.call(arguments,0);
    const tgt = f.targetElement;
    args.unshift(timestring(),'UTC:');
    if(tgt){
      tgt.classList.remove('error');
      tgt.innerText = args.join(' ');
    }
    else{
      args.unshift('Fossil status:');
      console.debug.apply(console,args);
    }
    return this;
  };
  /*
  ** Set default message.targetElement to #fossil-status-bar, if found.
  */
  global.fossil.message.targetElement =
    document.querySelector('#fossil-status-bar');
  /*
  ** By default fossil.error() sends its first argument to
  ** console.error(). If fossil.message.targetElement (yes,
  ** fossil.message) is set, it adds the 'error' CSS class to
  ** that element and sets its content as defined for message().
  **
  ** Returns this object.
  */
  global.fossil.error = function f(msg){
    const args = Array.prototype.slice.call(arguments,0);
    const tgt = global.fossil.message.targetElement;
    args.unshift(timestring(),'UTC:');
    if(tgt){
      tgt.classList.add('error');
      tgt.innerText = args.join(' ');
    }
    else{
      args.unshift('Fossil error:');







|
















|









|

|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  ** element, its innerText gets assigned to the concatenation of all
  ** arguments (with a space between each), and the CSS 'error' class is
  ** removed from the object. Pass it a falsy value to clear the target
  ** element.
  **
  ** Returns this object.
  */
  F.message = function f(msg){
    const args = Array.prototype.slice.call(arguments,0);
    const tgt = f.targetElement;
    args.unshift(timestring(),'UTC:');
    if(tgt){
      tgt.classList.remove('error');
      tgt.innerText = args.join(' ');
    }
    else{
      args.unshift('Fossil status:');
      console.debug.apply(console,args);
    }
    return this;
  };
  /*
  ** Set default message.targetElement to #fossil-status-bar, if found.
  */
  F.message.targetElement =
    document.querySelector('#fossil-status-bar');
  /*
  ** By default fossil.error() sends its first argument to
  ** console.error(). If fossil.message.targetElement (yes,
  ** fossil.message) is set, it adds the 'error' CSS class to
  ** that element and sets its content as defined for message().
  **
  ** Returns this object.
  */
  F.error = function f(msg){
    const args = Array.prototype.slice.call(arguments,0);
    const tgt = F.message.targetElement;
    args.unshift(timestring(),'UTC:');
    if(tgt){
      tgt.classList.add('error');
      tgt.innerText = args.join(' ');
    }
    else{
      args.unshift('Fossil error:');
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113





























114

     If the 2nd argument is an array, each encoded element is appended
     to that array and tgtArray is returned. The above object would be
     appended as ['a','=','1','&','b','=','2']. This form is used for
     building up parameter lists before join('')ing the array to create
     the result string.
  */
  global.fossil.encodeUrlArgs = function(obj,tgtArray){
    if(!obj) return '';
    const a = (tgtArray instanceof Array) ? tgtArray : [];
    let k, i = 0;
    for( k in obj ){
      if(i++) a.push('&');
      a.push(encodeURIComponent(k),
             '=',encodeURIComponent(obj[k]));
    }
    return a===tgtArray ? a : a.join('');
  };
  /**
     repoUrl( repoRelativePath [,urlParams] )

     Creates a URL by prepending this.rootPath to the given path
     (which must be relative from the top of the site, without a
     leading slash). If urlParams is a string, it must be
     paramters encoded in the form "key=val&key2=val2...", WITHOUT
     a leading '?'. If it's an object, all of its properties get
     appended to the URL in that form.
  */
  global.fossil.repoUrl = function(path,urlParams){
    if(!urlParams) return this.rootPath+path;
    const url=[this.rootPath,path];
    url.push('?');
    if('string'===typeof urlParams) url.push(urlParams);
    else if('object'===typeof urlParams){
      this.encodeUrlArgs(urlParams, url);
    }
    return url.join('');
  };





























})(window);







|




















|









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

     If the 2nd argument is an array, each encoded element is appended
     to that array and tgtArray is returned. The above object would be
     appended as ['a','=','1','&','b','=','2']. This form is used for
     building up parameter lists before join('')ing the array to create
     the result string.
  */
  F.encodeUrlArgs = function(obj,tgtArray){
    if(!obj) return '';
    const a = (tgtArray instanceof Array) ? tgtArray : [];
    let k, i = 0;
    for( k in obj ){
      if(i++) a.push('&');
      a.push(encodeURIComponent(k),
             '=',encodeURIComponent(obj[k]));
    }
    return a===tgtArray ? a : a.join('');
  };
  /**
     repoUrl( repoRelativePath [,urlParams] )

     Creates a URL by prepending this.rootPath to the given path
     (which must be relative from the top of the site, without a
     leading slash). If urlParams is a string, it must be
     paramters encoded in the form "key=val&key2=val2...", WITHOUT
     a leading '?'. If it's an object, all of its properties get
     appended to the URL in that form.
  */
  F.repoUrl = function(path,urlParams){
    if(!urlParams) return this.rootPath+path;
    const url=[this.rootPath,path];
    url.push('?');
    if('string'===typeof urlParams) url.push(urlParams);
    else if('object'===typeof urlParams){
      this.encodeUrlArgs(urlParams, url);
    }
    return url.join('');
  };

  /**
     Returns true if v appears to be a plain object.
  */
  F.isObject = function(v){
    return v &&
      (v instanceof Object) &&
      ('[object Object]' === Object.prototype.toString.apply(v) );
  };

  /**
     For each object argument, this function combines their properties,
     using a last-one-wins policy, and returns a new object with the
     combined properties. If passed a single object, it effectively
     shallowly clones that object.
  */
  F.mergeLastWins = function(){
    var k, o, i;
    const n = arguments.length, rc={};
    for(i = 0; i < n; ++i){
      if(!F.isObject(o = arguments[i])) continue;
      for( k in o ){
        if(o.hasOwnProperty(k)) rc[k] = o[k];
      }
    }
    return rc;
  };


})(window);
Added src/fossil.confirmer.js.














































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/**************************************************************
Confirmer is a utility which provides an alternative to confirmation
dialog boxes and "check this checkbox to confirm action" widgets. It
acts by modifying a button to require two clicks within a certain
time, with the second click acting as a confirmation of the first. If
the second click does not come within a specified timeout then the
action is not confirmed.

Usage:

fossil.confirmer(domElement, options);

Usually:

fossil.confirmer(element, {
  onconfirm: function(){
    // this === the element.
    // Do whatever the element would normally do when
    // clicked.
  }
});

Options:

	.initialText = initial text of the element. Defaults
    to the result of the element's .value (for INPUT tags) or
    innerHTML (for everything else).

	.confirmText = text to show when in "confirm mode".
	Default=("Confirm: "+initialText), or something similar.

	.timeout = Number of milliseconds to wait for confirmation.
	Default=3000.

	.onconfirm = function to call when clicked in confirm mode.  Default
	= undefined. The function's "this" is the the DOM element to which the
	countdown applies.

	.ontimeout = function to call when confirm is not issued. Default =
	undefined. The function's "this" is the DOM element to which the
	countdown applies.

	.onactivate = function to call when item is clicked, but only if the
	item is not currently in countdown mode. This is called (and must
	return) before the countdown starts. The function's "this" is the
	DOM element to which the countdown applies. This can be used, e.g.,
  to change the element's text or CSS classes.

	.classInitial = optional CSS class string (default='') which
	is added to the element during its "initial" state (the state
	it is in when it is not waiting on a timeout). When the target
	is activated (waiting on a timeout) this class is removed.
	In the case of a timeout, this class is added *before* the
	.ontimeout handler is called.

	.classActivated = optional CSS class string (default='') which
	is added to the target when it is waiting on a timeout. When
	the target leaves timeout-wait mode, this class is removed.
	When timeout-wait mode is entered, this class is added *before*
	the .onactivate handler is called.

  .debug = boolean. If truthy, it sends some debug output
  to the dev console to track what it's doing.


Due to the nature of multi-threaded code, it is potentially possible
that confirmation and timeout actions BOTH happen if the user triggers
the associated action at "just the right millisecond" before the
timeout is triggered.

To change the default option values, modify the
fossil.confirmer.defaultOpts object.

Terse Change history:

20200506:
  - Ported from jQuery to plain JS.

- 20181112:
  - extended to support certain INPUT elements.
  - made default opts configurable.

- 20070717: initial jQuery-based impl.
*/
(function(F/*the fossil object*/){
  "use strict";

  F.confirmer = function f(elem,opt){

    const dbg = opt.debug
          ? function(){console.debug.apply(console,arguments)}
          : function(){};
    dbg("confirmer opt =",opt);
    if(!f.Holder){
      f.isInput = (e)=>/^(input|textarea)$/i.test(e.nodeName);
      f.Holder = function(target,opt){
        const self = this;
        self.target = target;
        self.opt = opt;
        self.timerID = undefined;
        self.state = this.states.initial;
        const isInput = f.isInput(target);
        const updateText = function(msg){
          if(isInput) target.value = msg;
          else target.innerHTML = msg;
        }
        updateText(self.opt.initialText);
        this.setClasses(false);
		    this.doTimeout = function() {
			    this.timerID = undefined;
			    if( this.state != this.states.waiting ) {
				    // it was already confirmed
				    return;
			    }
			    this.setClasses( false );
			    this.state = this.states.initial;
			    dbg("Timeout triggered.");
			    updateText(this.opt.initialText);
			    if( this.opt.ontimeout ) {
				    this.opt.ontimeout.call(this.target);
			    }
		    };
        target.addEventListener(
          'click', function(){
			      switch( self.state ) {
				    case( self.states.waiting ):
					    if( undefined !== self.timerID ) clearTimeout( self.timerID );
					    self.state = self.states.initial;
					    self.setClasses( false );
					    dbg("Confirmed");
					    updateText(self.opt.initialText);
					    if( self.opt.onconfirm ) self.opt.onconfirm.call(self.target);
					    break;
				    case( self.states.initial ):
					    self.setClasses( true );
					    if( self.opt.onactivate ) self.opt.onactivate.call( self.target );
					    self.state = self.states.waiting;
					    dbg("Waiting "+self.opt.timeout+"ms on confirmation...");
					    updateText( self.opt.confirmText );
					    self.timerID = setTimeout(function(){self.doTimeout();},self.opt.timeout );
					    break;
				    default: // can't happen.
					    break;
			      }
          }, false
        );
      };
      f.Holder.prototype = {
        states:{
          initial: 0, waiting: 1
        },
        setClasses: function(activated) {
			    if( activated ) {
				    if( this.opt.classActivated ) {
					    this.target.addClass( this.opt.classActivated );
				    }
				    if( this.opt.classInitial ) {
					    this.target.removeClass( this.opt.classInitial );
				    }
			    } else {
				    if( this.opt.classInitial ) {
					    this.target.addClass( this.opt.classInitial );
				    }
				    if( this.opt.classActivated ) {
					    this.target.removeClass( this.opt.classActivated );
				    }
			    }
		    }
        
      };
    }/*static init*/
    opt = F.mergeLastWins(f.defaultOpts,{
      initialText: (
        f.isInput(elem) ? elem.value : elem.innerHTML
      ) || "PLEASE SET .initialText"
    },opt);
    if(!opt.confirmText){
      opt.confirmText = "Confirm: "+opt.initialText;
    }
    new f.Holder(elem,opt);
    return this;
  };
  /**
     The default options for initConfirmer(). Tweak them to set the
     defaults. A couple of them (initialText and confirmText) are
     dynamically-generated, and can't reasonably be set in the
     defaults.
  */
  F.confirmer.defaultOpts = {
	  timeout:3000,
	  onconfirm:undefined,
	  ontimeout:undefined,
	  onactivate:undefined,
	  classInitial:'',
	  classActivated:'',
	  debug:true
  };

})(window.fossil);
Changes to src/fossil.page.fileedit.js.
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
    diffButtons.querySelector('button.unified').addEventListener(
      "click",(e)=>P.diff(false), false
    );
    P.e.btnCommit.addEventListener(
      "click",(e)=>P.commit(), false
    );
    if(P.e.btnReload){
      P.e.btnReload.addEventListener(

        "click",(e)=>P.loadFile(), false
      );
    }
    /**
       Cosmetic: jump through some hoops to enable/disable
       certain preview options depending on the current
       preview mode...
    */
    const selectPreviewMode =







|
>
|
|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    diffButtons.querySelector('button.unified').addEventListener(
      "click",(e)=>P.diff(false), false
    );
    P.e.btnCommit.addEventListener(
      "click",(e)=>P.commit(), false
    );
    if(P.e.btnReload){
      F.confirmer(P.e.btnReload, {
        confirmText: "Really reload, losing edits?",
        onconfirm: (e)=>P.loadFile()
      });
    }
    /**
       Cosmetic: jump through some hoops to enable/disable
       certain preview options depending on the current
       preview mode...
    */
    const selectPreviewMode =
Changes to src/main.mk.
217
218
219
220
221
222
223

224
225
226
227
228
229
230
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \

  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \







>







217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
Changes to src/style.c.
1572
1573
1574
1575
1576
1577
1578

















void style_emit_script_tabs(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_dom(asInline);
    style_emit_script_builtin("fossil.tabs.js",asInline);
  }
}
























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
void style_emit_script_tabs(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_dom(asInline);
    style_emit_script_builtin("fossil.tabs.js",asInline);
  }
}

/*
** The first time this is called, it calls style_emit_script_dom(),
** passing it the given asInline value, and emits the JS code from the
** built-in file fossil.confirmer.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
*/
void style_emit_script_confirmer(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_dom(asInline);
    style_emit_script_builtin("fossil.confirmer.js",asInline);
  }
}
Changes to win/Makefile.mingw.
639
640
641
642
643
644
645

646
647
648
649
650
651
652
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \

  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \







>







639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
Changes to win/Makefile.msc.
546
547
548
549
550
551
552

553
554
555
556
557
558
559
        $(SRCDIR)\..\skins\xekri\header.txt \
        $(SRCDIR)\accordion.js \
        $(SRCDIR)\ci_edit.js \
        $(SRCDIR)\copybtn.js \
        $(SRCDIR)\diff.tcl \
        $(SRCDIR)\forum.js \
        $(SRCDIR)\fossil.bootstrap.js \

        $(SRCDIR)\fossil.dom.js \
        $(SRCDIR)\fossil.fetch.js \
        $(SRCDIR)\fossil.page.fileedit.js \
        $(SRCDIR)\fossil.tabs.js \
        $(SRCDIR)\graph.js \
        $(SRCDIR)\href.js \
        $(SRCDIR)\login.js \







>







546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
        $(SRCDIR)\..\skins\xekri\header.txt \
        $(SRCDIR)\accordion.js \
        $(SRCDIR)\ci_edit.js \
        $(SRCDIR)\copybtn.js \
        $(SRCDIR)\diff.tcl \
        $(SRCDIR)\forum.js \
        $(SRCDIR)\fossil.bootstrap.js \
        $(SRCDIR)\fossil.confirmer.js \
        $(SRCDIR)\fossil.dom.js \
        $(SRCDIR)\fossil.fetch.js \
        $(SRCDIR)\fossil.page.fileedit.js \
        $(SRCDIR)\fossil.tabs.js \
        $(SRCDIR)\graph.js \
        $(SRCDIR)\href.js \
        $(SRCDIR)\login.js \