From bde653b0a57a5608e0c7213bed28c469cfecf129 Mon Sep 17 00:00:00 2001 From: Masahiro Nakao Date: Wed, 20 May 2026 17:52:34 +0900 Subject: [PATCH 1/3] Update history after canceling jobs --- lib/history.rb | 23 +++++++++++++++++++++++ run.rb | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/lib/history.rb b/lib/history.rb index ac474ce6..2c6df23e 100644 --- a/lib/history.rb +++ b/lib/history.rb @@ -1001,6 +1001,29 @@ def job_record_to_internal_hash(record) ) end + # Mark jobs canceled from the History page as completed in the local history. + def mark_jobs_as_canceled(db, job_ids) + Array(job_ids).each do |job_id| + record = find_job(db, job_id) + next unless record + + existing = job_record_to_internal_hash(record) + scheduler_data = { + "_status" => JOB_STATUS["completed"], + "_updated_time" => Time.now.iso8601 + } + + upsert_job( + db, + build_job_record( + existing: existing, + submit_data: nil, + scheduler_data: scheduler_data + ) + ) + end + end + # Update the status of all jobs that are not completed def update_status(conf, scheduler, bin, bin_overrides, ssh_wrapper, cluster_name) db = open_history_db(conf, cluster_name) diff --git a/run.rb b/run.rb index 9612e2d7..17e92ff6 100644 --- a/run.rb +++ b/run.rb @@ -567,6 +567,12 @@ def output_log(action, scheduler, **details) case params["action"] when "CancelJob" error_msg = scheduler.cancel(job_ids, bin, bin_overrides, ssh_wrapper) + if error_msg.nil? && File.exist?(history_db) + db = open_history_db(conf, conf.key?("clusters") ? cluster_name : nil) + db.transaction do + mark_jobs_as_canceled(db, job_ids) + end + end output_log("Cancel job", scheduler, cluster: cluster_name, job_ids: job_ids) when "DeleteInfo" if File.exist?(history_db) From 72b21499eff0cf541727b645f7d3e6a129fa9b8f Mon Sep 17 00:00:00 2001 From: geoffreyweal Date: Wed, 5 Aug 2026 18:01:24 +1200 Subject: [PATCH 2/3] update gitignore to exclude app and tmp --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index cc53b3f6..e85a4c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,14 @@ Gemfile.lock vendor/ .bundle TODO_ja.md + +# Site-local runtime files. conf.yml.erb is this deployment's configuration, +# apps/ holds its applications (apps_dir), and tmp/restart.txt is the Passenger +# restart trigger. All three are created for the local Docker OnDemand +# environment by ../docker_open_ondemand/bootstrap.sh; see its README. conf.yml.erb +apps/ +tmp/ # Regenerated from docs by misc/tests/run_tests.rb misc/tests/samples/ From 9b6e895c53f97003f2d8082def3d3e2a7933e766 Mon Sep 17 00:00:00 2001 From: geoffreyweal Date: Wed, 5 Aug 2026 18:02:35 +1200 Subject: [PATCH 3/3] Make Script Content Reactive to Form Currently, if the user makes a change in the script content and then makes a change in the form, all the changes that the user made to the script content will be reset. This update does two things: 1. The script content is reactive to the form. That is, if you make a change to the script content, it will be updated in the form. 2. By doing this, the user does not need to reset their script content if they reset the form. --- docs/application.html | 59 ++++- docs/application_ja.html | 59 ++++- docs/manual.html | 12 +- docs/manual_ja.html | 12 +- lib/form.rb | 167 ++++++++++-- misc/tests/README.md | 25 +- misc/tests/extract_samples.rb | 67 +++-- misc/tests/run_tests.rb | 28 +- misc/tests/test_script_patterns.rb | 231 ++++++++++++++++ public/form.js | 409 +++++++++++++++++++++++++++-- run.rb | 25 -- views/form.erb | 159 ++--------- 12 files changed, 974 insertions(+), 279 deletions(-) create mode 100644 misc/tests/test_script_patterns.rb diff --git a/docs/application.html b/docs/application.html index 171a9dae..685ab205 100644 --- a/docs/application.html +++ b/docs/application.html @@ -36,7 +36,7 @@
Contents
- + @@ -776,23 +776,56 @@

5.4. Special variables

  • #{OC_JOB_NAME}: Job Name defined in the header section
  • -

    5.5. Disable warning

    +

    5.5. Loading a script back into the form

    - If you try to modify a widget that affects the script or submit section after manually editing those sections, a warning like the one below will appear. - If you click "Discard and continue", the changes you made to the section will be discarded and a new script will be generated. + If you change any options in the form or header, this changes what is written in the contents script. Likewise, if you change what is written in the contents script, it will be updated if possible in the form or header.

    - warning

    - To disable this warning, set overwrite_warning: false in the configuration of the section where you want to disable the warning. + Three rules govern whether a line can be read back: +

    +
      +
    • It must start with literal text. A line beginning with an interpolation, such as #{command} -n 4, is never matched.
    • +
    • That leading text must be unique. Each pattern takes the first script line it matches, so two template lines sharing a prefix will both read the same one.
    • +
    • It must not use calc(), dirname() or basename(): those lose information and cannot be reversed. Such a line is still patched in place, but no field is attached to it. zeropadding() is the exception — see the table below.
    • +
    +

    + #SBATCH --time= is handled separately. It is decoded by a dedicated parser accepting every Slurm format (D-HH:MM:SS, D-HH:MM, D-HH, HH:MM:SS, MM:SS, MM), which fills the line's fields with days, hours, minutes and seconds — so they must appear in the template in that order. +

    +

    + The captured text is applied according to the widget: +

    + + + + + + + + + + +
    WidgetRead back from the script
    number, text, email, pathWritten straight into the input.
    select, radioThe option whose value (the second element of its options entry) equals the text is selected.
    checkboxSplit on the widget's separator, a comma by default. Matching options are ticked and the rest unticked, so deleting a value from the line clears its box.
    multi_selectNot applied. The badges keep what they held, and an edit to the line is rebuilt from them on the next widget change.
    +

    + Of the template functions, only zeropadding() can be read back: +

    + + + + + + + + + + +
    FunctionRead back from the script
    zeropadding(field, N)The padded digits are matched and the padding stripped, so run-0125.log puts 125 in the field. It only pads, so nothing is lost.
    calc()Not applied. A result has no single set of inputs: 28 could be any factor pair.
    dirname(), basename()Not applied. Each discards half the path, so the field's value cannot be rebuilt.
    zeropadding(calc(...), N)Not applied. The nested calc() is still not reversible.
    +

    + A zeropadding() line is only read back when every one of its interpolations is either plain or a bare zeropadding(field, N), and when the interpolations are separated by literal text. Two captures written side by side, as in #{zeropadding(h, 2)}#{zeropadding(m, 2)}, cannot be split apart again, so such a line is patched but not parsed. +

    +

    + Loading a script may also open a hidden section: if a matched field is disabled, the enable- actions controlling it are fired so the section appears with the value already in it. This happens only when no other field could have written the same line. If two fields both write lines starting #$ -o , for instance, a loaded #$ -o out.log could have come from either, so its value is filled in but no section is opened.

    -
    script:
    -  overwrite_warning: false
    -  content: |
    - -
    submit:
    -  overwrite_warning: false
    -  content: |

    6. Check section

    Variables defined in the form section can be validated in the check section using the Ruby language and the function oc_assert(condition, message). diff --git a/docs/application_ja.html b/docs/application_ja.html index c9084673..eab10c45 100644 --- a/docs/application_ja.html +++ b/docs/application_ja.html @@ -36,7 +36,7 @@

    目次
    - + @@ -720,22 +720,55 @@

    5.4. 特殊な変数

  • #{OC_JOB_NAME}: ヘッダで定義されているJob Name
  • -

    5.5. 警告の無効化

    +

    5.5. フォームへのスクリプトの読み戻し

    - scriptセクションやsubmitセクションを手動で変更した後に、それらを変更するウィジットを変更しようとすると、下記のような警告が表示されます。"Discard and continue"をクリックすると、各セクションに加えられた変更は消去され、新しいスクリプトが生成されます。 + formやheaderの項目を変更すると、それに応じてスクリプトの内容も変更されます。逆に、スクリプトの内容を変更した場合も、可能であればformやheaderの項目が更新されます。 +

    - warning + 行を読み戻せるかどうかは、次の3つの規則で決まります。 +

    +
      +
    • リテラルのテキストで始まること。#{command} -n 4のように補間から始まる行は、決して一致しません。
    • +
    • 先頭のテキストが一意であること。各パターンは最初に一致したスクリプトの行を使用するため、2つのテンプレートの行が同じ先頭を持つ場合、両方が同じ行を読み取ってしまいます。
    • +
    • calc()dirname()basename()を使用しないこと。これらは情報が失われるため逆算できません。そのような行も所定の位置で書き換えられますが、項目は関連付けられません。zeropadding()は例外です(後述の表を参照)。
    • +

    - この警告を無効化するには、無効化したいセクションの設定でoverwrite_warning: falseを指定します。 + #SBATCH --time=は個別に扱われます。この行は専用の解析処理で扱われ、Slurmのすべての形式(D-HH:MM:SSD-HH:MMD-HHHH:MM:SSMM:SSMM)を受け付けて、その行の項目を日、時、分、秒で埋めます。そのため、これらの項目はテンプレートにこの順序で記述する必要があります。 +

    +

    + キャプチャされたテキストは、対象のウィジットに応じて適用されます。 +

    + + + + + + + + + + +
    ウィジットスクリプトからの読み戻し
    number, text, email, path入力欄にそのまま書き込まれます。
    select, radio値(optionsの要素の2つ目)がテキストと等しい選択肢が選択されます。
    checkboxウィジットのseparator(既定はカンマ)で分割されます。一致する選択肢はチェックされ、それ以外はチェックが外れるため、行から値を削除するとその選択肢のチェックも外れます。
    multi_select適用されません。バッジは元の状態を保持し、その行への編集は次にウィジットを変更した時点でバッジの内容から再生成されます。
    +

    + テンプレートの関数のうち、読み戻せるのはzeropadding()だけです。 +

    + + + + + + + + + + +
    関数スクリプトからの読み戻し
    zeropadding(field, N)桁を埋めた数字に一致し、その埋めた分が取り除かれます。例えばrun-0125.logからは125が項目に入ります。桁を埋めるだけなので情報は失われません。
    calc()適用されません。計算結果からは元の値の組を一意に定められません。例えば28はどの因数の組でもあり得ます。
    dirname()basename()適用されません。いずれもパスの一部を捨てるため、項目の値を復元できません。
    zeropadding(calc(...), N)適用されません。入れ子になったcalc()は逆算できないままです。
    +

    + zeropadding()を含む行が読み戻されるのは、その行のすべての補間が、単純な補間またはzeropadding(field, N)そのものであり、かつ補間どうしがリテラルのテキストで区切られている場合だけです。#{zeropadding(h, 2)}#{zeropadding(m, 2)}のように2つの補間が隣接している場合は分割できないため、その行は書き換えの対象にはなりますが、読み戻しは行われません。 +

    +

    + スクリプトの読み込み時には、非表示の区画が開くこともあります。一致した項目が無効になっている場合、その項目を制御するenable-の動作が発火し、値が入った状態で区画が現れます。これは、他のどの項目も同じ行を書き出せない場合にのみ起こります。例えば2つの項目がどちらも#$ -o で始まる行を書き出す場合、読み込まれた#$ -o out.logはどちらの項目のものか判別できないため、値は入力されますが区画は開きません。

    - -
    script:
    -  overwrite_warning: false
    -  content: |
    - -
    submit:
    -  overwrite_warning: false
    -  content: |

    6. checkセクション

    diff --git a/docs/manual.html b/docs/manual.html index 2a9af308..b0400492 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -51,14 +51,14 @@

    3. Application Page

  • The "Script location," "Script name," and "Job name" in the header section specify the "Directory where the job script is stored," "Name of the job script file," and "Job name," respectively.
  • The "Cluster name" in the header section is only displayed if multiple job schedulers are configured. The job script will be submitted to the selected cluster.
  • An asterisk next to the label of a web form indicates that it is a required field.
  • -
  • If you try to modify a web form with a white background after manually editing the job script, a warning like the one shown below will appear. - If you click "Discard and continue", the changes made in the job script will be discarded and a new script will be generated. -

    - warning -

    +
  • A web form with a white background writes to the job script, and each such form owns one line of it. + Changing a form rewrites only that line, so any edits you have made elsewhere in the job script are kept. +
  • +
  • The reverse also holds: when you edit the job script by hand, the web forms update to match it about half a second after you stop typing. + Editing a line that no form owns simply leaves the forms alone.
  • If the background color of the web form is yellow, it indicates that neither the job script nor the script that performs processing before job submission will be modified.
  • -
  • If the background color of the web form is pink, it indicates that only the script that performs processing before job submission will be modified. If you try to modify these web forms after manually editing that script, the same warning described above will appear. +
  • If the background color of the web form is pink, it indicates that only the script that performs processing before job submission will be modified.

    Application page

    diff --git a/docs/manual_ja.html b/docs/manual_ja.html index 000bb858..9c707d9b 100644 --- a/docs/manual_ja.html +++ b/docs/manual_ja.html @@ -43,14 +43,14 @@

    3. アプリケーションページ

  • ヘッダの「Script location」と「Script name」と「Job name」は、それぞれ「ジョブスクリプトの保存先ディレクトリ」と「ジョブスクリプトのファイル名」と「ジョブ名」を記述します。
  • ヘッダの「Cluster name」は複数のジョブスケジューラを設定している場合のみ表示されます。選択されたクラスタにジョブスクリプトが投入されます。
  • Webフォームのラベルにアスタリスクがある場合、それは必須項目であることを表します。
  • -
  • ジョブスクリプトを手動で変更した後に、背景色が白色のWebフォームを変更しようとすると、下記のような警告が表示されます。 - "Discard and continue"をクリックすると、ジョブスクリプトに加えられた変更は消去され、新しいスクリプトが生成されます。 -

    - warning -

    +
  • 背景色が白色のWebフォームはジョブスクリプトを変更し、それぞれのフォームがスクリプトの1つの行を担当します。 + フォームを変更してもその行だけが書き換えられるため、ジョブスクリプトの他の箇所に加えた手動での変更は保持されます。 +
  • +
  • 連携は双方向です。ジョブスクリプトを手動で編集すると、入力を止めてから約0.5秒後にWebフォームがその内容に合わせて更新されます。 + どのフォームも担当していない行を編集した場合は、フォームは変更されません。
  • Webフォームの背景色が黄色は、ジョブスクリプトおよびジョブ投入前の処理を行うスクリプトを変更しないことを示します。
  • -
  • Webフォームの背景色がピンクは、ジョブ投入前の処理を行うスクリプトのみを変更することを示します。そのスクリプトを手動で変更した後に、これらのWebフォームを変更しようとすると、上記と同様の警告が表示されます。 +
  • Webフォームの背景色がピンクは、ジョブ投入前の処理を行うスクリプトのみを変更することを示します。

    Application page

    diff --git a/lib/form.rb b/lib/form.rb index 903f2407..59bdc807 100644 --- a/lib/form.rb +++ b/lib/form.rb @@ -145,8 +145,7 @@ def output_number_text_email_html(key, value, script_content, submit_content, ap 'submit' end if type - html << "onfocus=\"ocForm.storePreviousValue('#{id}')\" " \ - "oninput=\"ocForm.confirmOverwrite('#{type}', '#{id}', function(){ocForm.updateArea('#{type}', '#{id}');})\"" + html << "oninput=\"ocForm.updateArea('#{type}', '#{id}')\"" html << " style=\"background-color: #{@conf["submit_color"]};\"" if type == 'submit' else html << "style=\"background-color: #{@conf["non_script_color"]};\"" @@ -228,13 +227,32 @@ def escape_js_string(str) end # Output a JavaScript code based on a given yml, line in script, and matches data. + # Returns [show_js, pattern_js]: + # show_js - pushes the rendered line into selectedValues (script generation), + # pattern_js - registers the line in ocForm.scriptLinePatterns so form.js can + # patch just this line in place and parse it back into the widgets. def output_script_js(form, line, app_name, dir_name) line = normalize_interpolation(line) line = substitute_oc_constants(line, app_name, dir_name) + raw_line = line.dup line = escape_js_string(line) matches = line.scan(/\#\{.+?\}/) - return " selectedValues.push(\'#{line}\');\n" if matches.empty? + if matches.empty? + # A literal line has no widget behind it, but it still needs a pattern so + # patchScript() recognises it as template-owned rather than user-typed. + # literal:true marks it as never regenerated: since no widget can alter + # it, a difference from the template means the user edited it, and + # patchScript keeps their version. This must NOT be set on the + # prefix-only patterns emitted below for calc()/zeropadding() lines, + # which do change when their widgets change. + pattern_js = "" + unless raw_line.empty? + prefix_js = escape_js_string(raw_line) + pattern_js = " ocForm.scriptLinePatterns.push({prefix:'#{prefix_js}', regex:null, keys:[], widgets:[], separators:[], canHide:[], literal:true});\n" + end + return [" selectedValues.push(\'#{line}\');\n", pattern_js] + end keys = matches.flat_map do |str| inner = str[2..-2] # "#{time_1}" -> "time_1" @@ -317,9 +335,85 @@ def output_script_js(form, line, app_name, dir_name) can_hide_array = "[" + can_hide.map { |r| r }.join(", ") + "]" separators_array = "[" + separators.map { |s| s.nil? ? 'null' : "'#{s}'" }.join(", ") + "]" - return " ocForm.showLine(selectedValues, '#{line}', #{keys_array}, #{widgets_array}, #{can_hide_array}, #{separators_array});\n" + show_js = " ocForm.showLine(selectedValues, '#{line}', #{keys_array}, #{widgets_array}, #{can_hide_array}, #{separators_array});\n" + + # The prefix (text before the first #{...}) identifies this line in the + # script textarea. The regex additionally captures each widget's value so + # the line can be read back into the form. + has_complex = raw_line.match?(/\#\{(calc|zeropadding|dirname|basename)\(/) + raw_parts = raw_line.split(/\#\{[^}]+\}/, -1) + prefix = raw_parts[0] + # The interpolation expressions in order, e.g. ["days", "zeropadding(hours, 2)"]. + # Split and scan use the same [^}]+, so interps[i] is the expression that + # produced the capture between raw_parts[i] and raw_parts[i + 1]. + interps = raw_line.scan(/\#\{([^}]+)\}/).flatten + + # zeropadding(field, N) is the one function that can be inverted: it only + # pads, so the original value is recovered by matching digits. calc() + # loses its inputs, dirname()/basename() discard half the path, and a + # nested zeropadding(calc(...), N) inherits calc()'s problem. A line using + # only bare zeropadding() is therefore still parseable. + # + # exist_keys must also line up 1:1 with the captures — each bare + # zeropadding() contributes exactly one key, but a key naming a widget + # that does not exist is dropped above, which would shift every later + # capture onto the wrong field. + # + # Two captures with no literal text between them also cannot be split: + # "(\d+)(\d+)" against "0230" is ambiguous, and the first capture would + # greedily swallow the second field's padding. Such a line is left + # patch-only rather than parsed wrongly. + adjacent_captures = (1...(raw_parts.length - 1)).any? { |i| raw_parts[i].empty? } + + zeropad_only = has_complex && + !adjacent_captures && + exist_keys.length == interps.length && + interps.all? do |e| + e !~ /\A(?:calc|zeropadding|dirname|basename)\(/ || + e =~ /\Azeropadding\(\s*:?[A-Za-z_]\w*\s*,\s*\d+\s*\)\z/ + end + + pattern_js = "" + unless prefix.empty? + prefix_js = escape_js_string(prefix) + + if has_complex && raw_line.lstrip.start_with?("#SBATCH --time=") + # Keeps precedence over zeropad inversion: the dedicated parser accepts + # every Slurm --time format, which a regex built from one template line + # could not (a hand-typed "--time=60" would simply fail to match). + pattern_js = " ocForm.scriptLinePatterns.push({prefix:'#{prefix_js}', regex:null, keys:#{keys_array}, widgets:#{widgets_array}, separators:#{separators_array}, canHide:#{can_hide_array}, parseType:'slurm_time'});\n" + elsif has_complex && !zeropad_only + # A computed value cannot be inverted, so only the prefix is registered + # — enough to patch the line, not to parse it. + pattern_js = " ocForm.scriptLinePatterns.push({prefix:'#{prefix_js}', regex:null, keys:[], widgets:[], separators:[], canHide:[]});\n" + else + zero_pad = [] + regex_parts = [] + raw_parts.each_with_index do |part, i| + regex_parts << Regexp.escape(part) + next unless i < raw_parts.length - 1 + + if zeropad_only && interps[i] =~ /\Azeropadding\(/ + # A padded field is always digits, so match digits rather than + # anything — this also stops the capture swallowing the padding + # of a neighbouring field when two sit next to each other. + regex_parts << "(\\d+)" + zero_pad << "true" + else + # The last capture is greedy so a trailing value keeps any spaces. + regex_parts << (i < raw_parts.length - 2 ? "(.*?)" : "(.*)") + zero_pad << "false" + end + end + regex_str = ("^" + regex_parts.join("") + "$").gsub("/", "\\/") + extra = zero_pad.include?("true") ? ", zeroPad:[#{zero_pad.join(', ')}]" : "" + pattern_js = " ocForm.scriptLinePatterns.push({prefix:'#{prefix_js}', regex:/#{regex_str}/, keys:#{keys_array}, widgets:#{widgets_array}, separators:#{separators_array}, canHide:#{can_hide_array}#{extra}});\n" + end + end + + return [show_js, pattern_js] else - return " selectedValues.push('#{line}');\n" + return [" selectedValues.push('#{line}');\n", ""] end end @@ -339,8 +433,7 @@ def output_select_html(key, value, script_content, submit_content, app_name, dir 'submit' end if type - html << "onfocus=\"ocForm.storePreviousValue('#{key}')\" " \ - "onchange=\"ocForm.confirmOverwrite('#{type}', '#{key}', function(){ocForm.updateArea('#{type}', '#{key}');})\"" + html << "onchange=\"ocForm.updateArea('#{type}', '#{key}')\"" html << " style=\"background-color: #{@conf["submit_color"]};\"" if type == 'submit' else html << "onchange=\"ocForm.execDynamicWidget('#{key}')\" " \ @@ -460,7 +553,7 @@ def output_radio_html(key, value, script_content, submit_content, app_name, dir_ 'submit' end if type - html << "onchange=\"ocForm.confirmOverwrite('#{type}', '#{id}', function(){ocForm.updateArea('#{type}', '#{id}')})\" oninput=\"ocForm.storePreviousValue('#{id}')\"" + html << "onchange=\"ocForm.updateArea('#{type}', '#{id}')\"" html << " style=\"background-color: #{@conf["submit_button_color"]};\"" if type == 'submit' html << ">\n" else @@ -510,7 +603,7 @@ def output_checkbox_html(key, value, script_content, submit_content, app_name, d 'submit' end if type - html << "onchange=\"ocForm.confirmOverwrite('#{type}', '#{id}', function(){ocForm.updateArea('#{type}', '#{id}')})\"" + html << "onchange=\"ocForm.updateArea('#{type}', '#{id}')\"" html << " style=\"background-color: #{@conf["submit_button_color"]};\"" if type == 'submit' html << ">\n" else @@ -532,6 +625,24 @@ def output_checkbox_js(key, value) return !value['required'].is_a?(Array) && value['required'].to_s == "true" ? " ocForm.validateCheckboxForSubmit('#{key}');" : "" end + # Generate JS that populates ocForm.enabledBy: maps each field key to the checkbox + # option IDs that enable it. Used by parseScriptToWidgets() to reopen a collapsed + # section (e.g. "Show advanced options") when a loaded script sets a field inside it. + def output_enabled_by_js(key, options) + js = "" + return js if options.nil? + options.each_with_index do |option, i| + next unless option.is_a?(Array) + (option[2..-1] || []).each do |action| + next unless action.is_a?(String) && action.start_with?("enable-") + target = action.sub(/^enable-/, '') + enabler_id = "#{key}_#{i + 1}" + js += " (ocForm.enabledBy[#{target.to_json}] = ocForm.enabledBy[#{target.to_json}] || []).push(#{enabler_id.to_json});\n" + end + end + js + end + # Output a path widget. def output_path_html(key, value, script_content, submit_content, app_name, dir_name) favorites = value['favorites'] ? value['favorites'].select { |path| File.exist?(path) } : [] @@ -554,19 +665,14 @@ def output_path_html(key, value, script_content, submit_content, app_name, dir_n 'submit' end if type - html += "oninput=\"ocForm.confirmOverwrite('#{type}', '#{key}', function(){ocForm.updateArea('#{type}', '#{key}')})\" " - html += "onfocus=\"ocForm.storePreviousValue('#{key}')\"" + html += "oninput=\"ocForm.updateArea('#{type}', '#{key}')\"" html += " style=\"background-color: #{@conf["submit_color"]};\"" if type == 'submit' else html += "style=\"background-color: #{@conf["non_script_color"]};\"" end html += ">\n" html += "\n" - else - html += "onclick=\"ocForm.loadFiles('#{@script_name}', '#{current_path}', '#{key}', #{show_files}, '#{Dir.home}', true)\">Select Path\n" - end + html += "onclick=\"ocForm.loadFiles('#{@script_name}', '#{current_path}', '#{key}', #{show_files}, '#{Dir.home}', true)\">Select Path\n" html += <<~HTML @@ -101,23 +101,6 @@ - - +