Merge remote-tracking branch 'origin/electron'

This commit is contained in:
Joey Payne 2016-02-08 08:42:16 -07:00
commit a93abf3491
7 changed files with 849 additions and 362 deletions

View file

@ -30,6 +30,7 @@ import logging
import logging.handlers as lh
import plistlib
import codecs
import requests
from pprint import pprint
from utils import get_data_path, get_data_file_path
@ -128,20 +129,42 @@ class Setting(object):
self.scope = kwargs.pop('scope', 'local')
self.default_value = kwargs.pop('default_value', None)
self.label_suffix = kwargs.pop('label_suffix', '')
self.button = kwargs.pop('button', None)
self.button_callback = kwargs.pop('button_callback', None)
self.description = kwargs.pop('description', u'')
self.values = kwargs.pop('values', [])
self.exists = kwargs.pop('exists', True)
self.filter = kwargs.pop('filter', '.*')
self.factor = kwargs.pop('factor', 1)
self.filter_action = kwargs.pop('filter_action', 'None')
self.check_action = kwargs.pop('check_action', 'None')
self.action = kwargs.pop('action', None)
convert = kwargs.pop('convert', lambda x: x)
if not callable(convert):
convert = eval(convert)
def conv(x):
try:
x = convert(x)
except ValueError:
x = convert(float(x))
if isinstance(x, (int, float)) and not isinstance(x, bool):
x = x/self.factor
return convert(x)
self.convert = conv
self.set_extra_attributes_from_keyword_args(**kwargs)
if self.value is None:
self.value = self.default_value
if self.value:
self.value = self.convert(self.value)
self.save_path = kwargs.pop('save_path', u'')
self.get_file_information_from_url()
@ -187,11 +210,6 @@ class Setting(object):
return ''
def extract_file_path(self, version):
if self.extract_file:
return self.extract_file.format(version)
return u''
def set_extra_attributes_from_keyword_args(self, **kwargs):
for undefined_key, undefined_value in kwargs.items():
setattr(self, undefined_key, undefined_value)
@ -229,47 +247,6 @@ class Setting(object):
utils.move(abs_file, ex_path)
utils.rmtree(dir_name, ignore_errors=True)
def get_file_bytes(self, version):
fbytes = []
path = self.save_file_path(version)
file = self.extract_class(path,
*self.extract_args)
for extract_path, dest_path in zip(self.extract_files,
self.dest_files):
new_bytes = None
try:
extract_p = extract_path.format(version)
versions = re.findall('(\d+)\.(\d+)\.(\d+)', version)[0]
minor = int(versions[1])
if minor >= 12:
extract_p = extract_p.replace('node-webkit', 'nwjs')
if self.file_ext == '.gz':
new_bytes = file.extractfile(extract_p).read()
elif self.file_ext == '.zip':
new_bytes = file.read(extract_p)
except KeyError as e:
logger.error(str(e))
# dirty hack to support old versions of nw
if 'no item named' in str(e):
extract_path = '/'.join(extract_path.split('/')[1:])
try:
if self.file_ext == '.gz':
new_bytes = file.extractfile(extract_path).read()
elif self.file_ext == '.zip':
new_bytes = file.read(extract_path)
except KeyError as e:
logger.error(str(e))
if new_bytes is not None:
fbytes.append((dest_path, new_bytes))
return fbytes
def __repr__(self):
url = ''
if hasattr(self, 'url'):
@ -291,6 +268,8 @@ class CommandBase(object):
self.logger = None
self.output_package_json = True
self.settings = self.get_settings()
self.js_cmd_args = ''
self.js_window_init = ''
self._project_dir = ''
self._output_dir = ''
self._progress_text = ''
@ -301,28 +280,28 @@ class CommandBase(object):
def init(self):
self.logger = logging.getLogger('CMD logger')
self.update_nw_versions(None)
self.setup_nw_versions()
self.update_electron_versions(None)
self.setup_electron_versions()
def update_nw_versions(self, button):
def update_electron_versions(self, button):
self.progress_text = 'Updating nw versions...'
self.get_versions()
self.progress_text = '\nDone.\n'
def setup_nw_versions(self):
nw_version = self.get_setting('nw_version')
nw_version.values = []
def setup_electron_versions(self):
electron_version = self.get_setting('electron_version')
electron_version.values = []
try:
f = codecs.open(get_data_file_path('files/nw-versions.txt'), encoding='utf-8')
f = codecs.open(get_data_file_path('files/electron-versions.txt'), encoding='utf-8')
for line in f:
nw_version.values.append(line.strip())
electron_version.values.append(line.strip())
f.close()
except IOError:
nw_version.values.append(nw_version.default_value)
electron_version.values.append(electron_version.default_value)
def get_nw_versions(self):
nw_version = self.get_setting('nw_version')
return nw_version.values[:]
def get_electron_versions(self):
electron_version = self.get_setting('electron_version')
return electron_version.values[:]
def get_settings(self):
config_file = get_file('files/settings.cfg')
@ -401,30 +380,30 @@ class CommandBase(object):
union_versions = set()
for url in self.settings['version_info']['urls']:
response = request.urlopen(url)
html = response.read().decode('utf-8')
url = self.settings['version_info']['electron_url']
nw_version = self.get_setting('nw_version')
req = requests.get(url)
old_versions = set(nw_version.values)
old_versions = old_versions.union(union_versions)
new_versions = set(re.findall('(\S+) / \S+', html))
data = json.loads(req.text)
union_versions = old_versions.union(new_versions)
versions = [d['name'].split(' ')[-1][1:] for d in data]
electron_version = self.get_setting('electron_version')
old_versions = set(electron_version.values)
old_versions = old_versions.union(union_versions)
new_versions = set(versions)
union_versions = old_versions.union(new_versions)
versions = sorted(union_versions,
key=Version, reverse=True)
if len(versions) > 19:
#Cut off old versions
versions = versions[:-19]
nw_version.values = versions
electron_version.values = versions
f = None
try:
f = codecs.open(get_data_file_path('files/nw-versions.txt'), 'w', encoding='utf-8')
for v in nw_version.values:
f = codecs.open(get_data_file_path('files/electron-versions.txt'), 'w', encoding='utf-8')
for v in electron_version.values:
f.write(v+os.linesep)
f.close()
except IOError:
@ -444,10 +423,6 @@ class CommandBase(object):
path = setting.url.format(version, version)
versions = re.findall('(\d+)\.(\d+)\.(\d+)', version)[0]
minor = int(versions[1])
if minor >= 12:
path = path.replace('node-webkit', 'nwjs')
try:
return self.download_file(setting.url.format(version, version),
setting)
@ -467,7 +442,7 @@ class CommandBase(object):
p_json = [json_path]
else:
p_json = glob.glob(utils.path_join(self.project_dir(),
'package.json'))
'package.json'))
setting_list = []
if p_json:
json_str = ''
@ -486,10 +461,11 @@ class CommandBase(object):
def generate_json(self, global_json=False):
self.logger.info('Generating package.json...')
dic = {'webexe_settings': {}}
dic = {'webexe_settings': {},
'web_preferences': {}}
if not global_json:
dic.update({'webkit': {}, 'window': {}})
dic.update({'webkit': {}, 'window': {}, 'web': {}})
dic.update(self.original_packagejson)
for setting_name, setting in self.settings['app_settings'].items():
if setting.value is not None:
@ -499,18 +475,16 @@ class CommandBase(object):
for setting_name, setting in self.settings['window_settings'].items():
if setting.value is not None:
if 'height' in setting.name or 'width' in setting.name:
try:
dic['window'][setting_name] = int(setting.value)
except ValueError:
pass
else:
dic['window'][setting_name] = setting.value
dic['window'][setting_name] = setting.value
for setting_name, setting in self.settings['webkit_settings'].items():
if setting.value is not None:
dic['webkit'][setting_name] = setting.value
for setting_name, setting in self.settings['web_preferences'].items():
if setting.value is not None:
dic['web'][setting_name] = setting.value
if not global_json:
dl_export_items = (list(self.settings['download_settings'].items()) +
list(self.settings['export_settings'].items()) +
@ -525,6 +499,33 @@ class CommandBase(object):
if setting.value is not None:
dic['webexe_settings'][setting_name] = setting.value
dic['main'] = 'main.js'
js_settings = {'webPreferences': {}}
for setting_name, setting in self.settings['window_settings'].items():
js_settings[utils.to_camel_case(setting_name)] = setting.convert(setting.value)
for setting_name, setting in self.settings['web_preferences'].items():
js_settings['webPreferences'][utils.to_camel_case(setting_name)] = setting.convert(setting.value)
ignored_app_settings = set(['main_html', 'app_name', 'description', 'version', 'user_agent'])
self.js_cmd_args = ''
for setting_name, setting in self.settings['app_settings'].items():
if setting_name not in ignored_app_settings:
if setting.value:
js_name = setting_name.replace('_', '-')
if setting.type == 'check':
self.js_cmd_args += 'app.commandLine.appendSwitch("'+js_name+'");\n'
else:
value = setting.value
self.js_cmd_args += 'app.commandLine.appendSwitch("'+js_name+'", "'+value+'");\n'
self.js_window_init = 'var mainWindow = new BrowserWindow('+json.dumps(js_settings, indent=4)+');'
s = json.dumps(dic, indent=4)
return s
@ -577,7 +578,7 @@ class CommandBase(object):
setting.type == 'string' or
setting.type == 'folder'):
val_str = self.convert_val_to_str(new_dic[item])
setting.value = val_str
setting.value = setting.convert(val_str)
if setting.type == 'strings':
strs = self.convert_val_to_str(new_dic[item]).split(',')
setting.value = strs
@ -588,12 +589,14 @@ class CommandBase(object):
setting.value = val_str
if setting.type == 'range':
setting.value = new_dic[item]
if setting.type == 'color':
setting.value = new_dic[item]
if isinstance(new_dic[item], dict):
stack.append((item, new_dic[item]))
return setting_list
def selected_version(self):
return self.get_setting('nw_version').value
return self.get_setting('electron_version').value
def extract_files(self):
self.extract_error = None
@ -607,14 +610,6 @@ class CommandBase(object):
extract_path = get_data_path('files/'+setting.name)
setting.extract(extract_path, version)
#if os.path.exists(save_file_path):
# setting_fbytes = setting.get_file_bytes(version)
# for dest_file, fbytes in setting_fbytes:
# path = utils.path_join(extract_path, dest_file)
# with open(path, 'wb+') as d:
# d.write(fbytes)
# self.progress_text += '.'
self.progress_text += '.'
except (tarfile.ReadError, zipfile.BadZipfile) as e:
@ -679,23 +674,60 @@ class CommandBase(object):
json_file = utils.path_join(self.project_dir(), 'package.json')
global_json = utils.get_data_file_path('files/global.json')
js_template = get_file('files/mainjstemplate.js')
main_js_file = utils.path_join(self.project_dir(), 'main.js')
if self.output_package_json:
with codecs.open(json_file, 'w+', encoding='utf-8') as f:
f.write(self.generate_json())
with codecs.open(global_json, 'w+', encoding='utf-8') as f:
f.write(self.generate_json(global_json=True))
js_string = codecs.open(js_template, encoding='utf-8').read()
inject_js_start = ''
inject_js_end = ''
inject_start_file = self.get_setting('inject_js_start').value
inject_start_file = utils.path_join(self.project_dir(),
inject_start_file)
if os.path.exists(inject_start_file):
inject_js_start = codecs.open(inject_start_file, encoding='utf-8').read()
inject_end_file = self.get_setting('inject_js_end').value
inject_end_file = utils.path_join(self.project_dir(),
inject_end_file)
if os.path.exists(inject_end_file):
inject_js_end = codecs.open(inject_end_file, encoding='utf-8').read()
main_html = self.get_setting('main_html')
user_agent = self.get_setting('user_agent')
js_string = js_string.replace('{{command_line_switches}}', self.js_cmd_args)
js_string = js_string.replace('{{user_agent}}', '"'+user_agent.value+'"')
js_string = js_string.replace('{{main_html}}', main_html.value)
js_string = js_string.replace('{{main_window_line}}', self.js_window_init)
js_string = js_string.replace('{{app_name}}', self.project_name())
js_string = js_string.replace('{{inject_js_start}}', inject_js_start)
js_string = js_string.replace('{{inject_js_end}}', inject_js_end)
with codecs.open(main_js_file, 'w+', encoding='utf-8') as f:
f.write(js_string)
zip_file = utils.path_join(temp_dir, self.project_name()+'.nw')
app_nw_folder = utils.path_join(temp_dir, self.project_name()+'.nwf')
utils.copytree(self.project_dir(), app_nw_folder,
ignore=shutil.ignore_patterns(output_dir))
if self.project_dir() in self.output_dir():
utils.copytree(self.project_dir(), app_nw_folder,
ignore=shutil.ignore_patterns(os.path.basename(self.output_dir())))
else:
utils.copytree(self.project_dir(), app_nw_folder)
zip_files(zip_file, self.project_dir(), exclude_paths=[output_dir])
for ex_setting in self.settings['export_settings'].values():
if ex_setting.value:
self.progress_text = '\n'
@ -724,14 +756,9 @@ class CommandBase(object):
app_path = utils.path_join(export_dest,
self.project_name()+'.app')
try:
utils.move(utils.path_join(export_dest,
'nwjs.app'),
app_path)
except IOError:
utils.move(utils.path_join(export_dest,
'node-webkit.app'),
app_path)
utils.move(utils.path_join(export_dest,
'Electron.app'),
app_path)
plist_path = utils.path_join(app_path, 'Contents', 'Info.plist')
@ -751,16 +778,18 @@ class CommandBase(object):
app_nw_res = utils.path_join(app_path,
'Contents',
'Resources',
'app.nw')
'default_app')
if os.path.exists(app_nw_res):
utils.rmtree(app_nw_res)
utils.copytree(app_nw_folder, app_nw_res)
if uncompressed:
utils.copytree(app_nw_folder, app_nw_res)
else:
utils.copy(zip_file, app_nw_res)
self.create_icns_for_app(utils.path_join(app_path,
'Contents',
'Resources',
'nw.icns'))
'Contents',
'Resources',
'atom.icns'))
self.progress_text += '.'
else:
@ -771,7 +800,7 @@ class CommandBase(object):
windows = True
nw_path = utils.path_join(export_dest,
ex_setting.dest_files[0])
ex_setting.binary_location)
if windows:
self.replace_icon_in_exe(nw_path)
@ -779,18 +808,28 @@ class CommandBase(object):
self.compress_nw(nw_path)
dest_binary_path = utils.path_join(export_dest,
self.project_name() +
ext)
self.project_name() +
ext)
if 'linux' in ex_setting.name:
self.make_desktop_file(dest_binary_path, export_dest)
join_files(dest_binary_path, nw_path, zip_file)
utils.move(nw_path, dest_binary_path)
app_nw_res = utils.path_join(export_dest,
'resources',
'default_app')
if os.path.exists(app_nw_res):
utils.rmtree(app_nw_res)
utils.copytree(app_nw_folder, app_nw_res)
sevenfivefive = (stat.S_IRWXU |
stat.S_IRGRP |
stat.S_IXGRP |
stat.S_IROTH |
stat.S_IXOTH)
os.chmod(dest_binary_path, sevenfivefive)
self.progress_text += '.'
@ -1047,6 +1086,8 @@ class CommandBase(object):
os.path.sep,
self.project_name())
self.delete_files()
else:
print('Export failed. Check the log at {} or run again with --verbose.'.format(LOG_FILENAME))
def get_export_options(self):
options = []
@ -1073,6 +1114,11 @@ class CommandBase(object):
self.progress_text = 'Extracting files.'
return self.extract_files()
def get_redirected_url(self, url):
opener = request.build_opener(request.HTTPRedirectHandler)
req = opener.open(url)
return req.url
def download_file(self, path, setting):
self.logger.info(u'Downloading file {}.'.format(path))
@ -1080,9 +1126,7 @@ class CommandBase(object):
versions = re.findall('v(\d+)\.(\d+)\.(\d+)', path)[0]
minor = int(versions[1])
if minor >= 12:
path = path.replace('node-webkit', 'nwjs')
path = self.get_redirected_url(path)
url = path
file_name = setting.save_file_path(self.selected_version(), location)
@ -1099,6 +1143,7 @@ class CommandBase(object):
forced = self.get_setting('force_download').value
if (archive_exists or dest_files_exist) and not forced:
print('File exists.')
self.logger.info(u'File {} already downloaded. Continuing...'.format(path))
return self.continue_downloading_or_extract()
elif tmp_exists and (os.stat(tmp_file).st_size > 0):
@ -1109,7 +1154,7 @@ class CommandBase(object):
web_file = request.urlopen(url)
f = open(tmp_file, 'ab')
meta = web_file.info()
file_size = tmp_size + int(meta.getheaders("Content-Length")[0])
file_size = tmp_size + int(meta["Content-Length"])
version = self.selected_version()
version_file = self.settings['base_url'].format(version)
@ -1155,10 +1200,9 @@ class CommandBase(object):
def delete_files(self):
for ex_setting in self.settings['export_settings'].values():
for dest_file in ex_setting.dest_files:
f_path = get_data_file_path('files/{}/{}'.format(ex_setting.name, dest_file))
if os.path.exists(f_path):
os.remove(f_path)
f_path = get_data_file_path('files/{}/'.format(ex_setting.name))
if os.path.exists(f_path):
utils.rmtree(f_path)
class ArgParser(argparse.ArgumentParser):

BIN
files/images/web_prefs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

189
files/images/web_prefs.svg Normal file
View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/home/joey/Projects/Web2Executable/files/images/download_settings.png"
sodipodi:docname="web_prefs.svg"
inkscape:version="0.91+devel r"
version="1.1"
id="SVGRoot"
viewBox="0 0 16 16"
height="16px"
width="16px">
<sodipodi:namedview
inkscape:grid-bbox="true"
inkscape:window-maximized="1"
inkscape:window-y="24"
inkscape:window-x="65"
inkscape:window-height="1056"
inkscape:window-width="1855"
showgrid="false"
inkscape:current-layer="layer1"
inkscape:document-units="px"
inkscape:cy="4.3433224"
inkscape:cx="-14.611057"
inkscape:zoom="16.000001"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<defs
id="defs4143">
<linearGradient
id="linearGradient4772"
inkscape:collect="always">
<stop
id="stop4774"
offset="0"
style="stop-color:#d3d3d3;stop-opacity:1;" />
<stop
id="stop4776"
offset="1"
style="stop-color:#d3d3d3;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient4730"
inkscape:collect="always">
<stop
id="stop4732"
offset="0"
style="stop-color:#3d3d3e;stop-opacity:1;" />
<stop
id="stop4734"
offset="1"
style="stop-color:#3d3d3e;stop-opacity:0;" />
</linearGradient>
<linearGradient
gradientTransform="matrix(1.0439827,0,0,1.0439827,-0.33554965,-0.28661264)"
gradientUnits="userSpaceOnUse"
y2="-100.05997"
x2="-124.15217"
y1="13.064024"
x1="13.593119"
id="linearGradient4736"
xlink:href="#linearGradient4730"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="26.952219"
x2="46.513737"
y1="3.2154982"
x1="6.4024119"
id="linearGradient4780"
xlink:href="#linearGradient4772"
inkscape:collect="always" />
</defs>
<metadata
id="metadata4146">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
ry="2.055341"
y="1.181488"
x="0.93680429"
height="13.637024"
width="14.126391"
id="rect4700"
style="opacity:1;fill:url(#linearGradient4736);fill-opacity:1;stroke:none;stroke-width:62.59950638;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
id="g4365"
transform="translate(5.9604645e-7,0.00979745)">
<rect
ry="0.48212972"
y="2.7451203"
x="2.6081035"
height="2.8339148"
width="2.8339148"
id="rect4229"
style="opacity:1;fill:#4a89dc;fill-opacity:1;stroke:#3d6bc5;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
ry="0.48212972"
y="10.40137"
x="10.557981"
height="2.8339148"
width="2.8339148"
id="rect4229-8"
style="opacity:1;fill:#8cc152;fill-opacity:1;stroke:#6e9e38;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="opacity:1;fill:#f6bb42;fill-opacity:1;stroke:#efa50c;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4261"
width="2.8339148"
height="2.8339148"
x="10.557981"
y="2.7451203"
ry="0.48212972" />
<rect
style="opacity:1;fill:#dc4a4a;fill-opacity:1;stroke:#c53d3d;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4268"
width="2.8339148"
height="2.8339148"
x="2.6081035"
y="6.55762"
ry="0.48212972" />
<rect
style="opacity:1;fill:#a752c1;fill-opacity:1;stroke:#80389e;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4270"
width="2.8339148"
height="2.8339148"
x="6.5579805"
y="10.40137"
ry="0.48212972" />
<rect
ry="0.48212972"
y="6.55762"
x="10.557981"
height="2.8339148"
width="2.8339148"
id="rect4272"
style="opacity:1;fill:#d770ad;fill-opacity:1;stroke:#c4378a;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
ry="0.48212972"
y="10.40137"
x="2.6081035"
height="2.8339148"
width="2.8339148"
id="rect4314"
style="opacity:1;fill:#919191;fill-opacity:1;stroke:#747474;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
ry="0.48212972"
y="6.55762"
x="6.5579805"
height="2.8339148"
width="2.8339148"
id="rect4316"
style="opacity:1;fill:#919191;fill-opacity:1;stroke:#747474;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="opacity:1;fill:#919191;fill-opacity:1;stroke:#747474;stroke-width:0.38183275;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4318"
width="2.8339148"
height="2.8339148"
x="6.5579805"
y="2.7451203"
ry="0.48212972" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

46
files/mainjstemplate.js Normal file
View file

@ -0,0 +1,46 @@
var app = require('app'); // Module to control application life.
var BrowserWindow = require('browser-window'); // Module to create native browser window.
{{command_line_switches}}
{{inject_js_start}}
// Report crashes to our server.
require('crash-reporter').start();
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
app.quit();
}
});
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
// Create the browser window.
{{main_window_line}}
// and load the index.html of the app.
mainWindow.loadUrl('file://' + __dirname + '/{{main_html}}');
mainWindow.webContents.setUserAgent({{user_agent}});
mainWindow.webContents.on('did-finish-load',function(){
mainWindow.setTitle("{{app_name}}");
{{inject_js_end}}
});
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
});

View file

@ -1,27 +1,22 @@
base_url='http://dl.nwjs.io/v{}/'
win_32_dir_prefix = 'node-webkit-v{}-win-ia32'
mac_32_dir_prefix = 'node-webkit-v{}-osx-ia32'
linux_32_dir_prefix = 'node-webkit-v{}-linux-ia32'
base_url='https://github.com/atom/electron/releases/download/v{}/'
win_32_dir_prefix = 'electron-v{}-win32-ia32'
mac_32_dir_prefix = 'electron-v{}-darwin-ia32'
linux_32_dir_prefix = 'electron-v{}-linux-ia32'
win_64_dir_prefix = 'electron-v{}-win32-x64'
mac_64_dir_prefix = 'electron-v{}-darwin-x64'
linux_64_dir_prefix = 'electron-v{}-linux-x64'
win_64_dir_prefix = 'node-webkit-v{}-win-x64'
mac_64_dir_prefix = 'node-webkit-v{}-osx-x64'
linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
[setting_groups]
[[app_settings]]
[[[main]]]
[[[main_html]]]
display_name='Main html file'
required=True
type='file'
file_types='*.html *.php *.htm'
description='Main html file relative to the project directory.'
[[[name]]]
display_name='Name'
required=True
type='string'
description='The name in the internal package.json. Must be alpha-numeric with no spaces.'
filter='[a-z0-9_\-\.]+'
filter_action='lower'
[[[app_name]]]
display_name='App Name'
required=False
@ -32,67 +27,121 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
type='string'
[[[version]]]
default_value='0.1.0'
filter='[a-z0-9\.]+'
type='string'
[[[keywords]]]
default_value=''
type='string'
[[[nodejs]]]
display_name='Include Nodejs'
default_value=True
type='check'
[[[node-main]]]
display_name='Node Main'
default_value=''
type='file'
file_types='*.js'
description='A path to a nodejs script file that will be executed on startup.'
[[[single-instance]]]
display_name='Single Instance'
default_value=True
type='check'
description='Restrict the app to run with only a single instance allowed at a time.'
[[[user-agent]]]
description='Version of your application.'
[[[user_agent]]]
display_name='User Agent'
default_value=''
type='string'
description='Overrides the User-Agent header in http requests.'
[[[node-remote]]]
display_name='Node Remote'
default_value=''
type='string'
description='Enable calling node in remote pages. See the node-webkit manifest format for more info.'
[[[chromium-args]]]
description='Overrides the User-Agent header in http requests.\n\nExample:\nMozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36'
[[[chromium_args]]]
display_name='Chromium Args'
default_value=''
type='string'
description='Specify chromium command line arguments. Example value: "--disable-accelerated-video --force-cpu-draw"'
[[[js-flags]]]
description='Specify chromium command line arguments.\nExample value: "--disable-accelerated-video --force-cpu-draw"'
[[[js_flags]]]
display_name='JS Flags'
default_value=''
type='string'
description='Specify flags passed to the js engine. Example value: "--harmony_proxies --harmony_collecions"'
[[[inject-js-start]]]
description='Specify flags passed to the js engine.\nExample value: "--harmony_proxies --harmony_collections"'
[[[inject_js_start]]]
display_name='Inject JS Start'
default_value=''
type='file'
file_types='*.js'
description='A path to a js file that will be executed before any other script is run.'
[[[inject-js-end]]]
description='A path to a js file that will be executed before any other\nscript is run. Will be inserted into generated main.js.'
[[[inject_js_end]]]
display_name='Inject JS End'
default_value=''
type='file'
file_types='*.js'
description='A path to a js file that will be executed after the DOM is loaded.'
[[[snapshot]]]
description='A path to a js file that will be executed after the DOM is\nloaded. Will be inserted into generated main.js.'
[[[client_certificate]]]
display_name='Client Certificate'
default_value=''
type='file'
file_types='*.bin'
description='A path to a binary file compiled with snapshot. Used if you don\'t want your code to be exposed.'
[[[additional_trust_anchors]]]
display_name='Trust Anchors'
description='Sets the path of client certificate file.'
[[[ignore_certificate_errors]]]
display_name='Ignore Cert. Errors'
default_value=False
type='check'
description='Ignores certificate related errors.'
[[[remote_debugging_port]]]
display_name='Remote Debug Port'
default_value=''
type='strings'
description='A list of PEM-encoded certificates. Used as additional root certificates for validation to allow connecting to services using a self-signed certificate.'
type='string'
filter='[0-9]+'
description='Enables remote debugging over HTTP on the port.'
[[[ignore_connections_limit]]]
display_name='Ignore Conn. Limit'
default_value=''
type='string'
description='Ignore the connection limit for domains list separated by a comma.\nExample: www.google.ca,www.bing.com'
[[[disable_http_cache]]]
display_name='Disable Http Cache'
default_value=False
type='check'
description='Disables the disk cache for HTTP requests.'
[[[proxy_server]]]
default_value=''
type='string'
description='Use a specified proxy server, which overrides the system\nsetting. This switch only affects requests with HTTP protocol,\nincluding HTTPS and WebSocket requests. It is also noteworthy\nthat not all proxy servers support HTTPS and WebSocket requests.\n\nExamples:\n231.114.156.200:4554\nmyserver.com:3423'
[[[proxy_bypass_list]]]
display_name='Proxy Bypass'
default_value=''
type='string'
description='Instructs Electron to bypass the proxy server for the given\nsemi-colon-separated list of hosts. This flag has an effect only\nif used in tandem with Proxy Server.\n\nExample:\n<local>;*.google.com;*foo.com;1.2.3.4:5678\n\nThe example will use the proxy server for all hosts except for\nlocal addresses (localhost, 127.0.0.1 etc.), google.com subdomains, hosts that\ncontain the suffix foo.com and anything at 1.2.3.4:5678.'
[[[proxy_pac_url]]]
display_name='Proxy Pac URL'
default_value=''
type='string'
description='Uses the PAC script at the specified URL.'
[[[no_proxy_server]]]
display_name='No Proxy'
default_value=False
type='check'
description="Don't use a proxy server and always make direct connections.\nOverrides any other proxy server settings."
[[[host_rules]]]
default_value=''
type='string'
description='A comma-separated list of rules that control how hostnames are mapped.\n\nFor example:\n\n- "MAP * 127.0.0.1" Forces all hostnames to be mapped to 127.0.0.1\n- "MAP *.google.com proxy" Forces all google.com subdomains to be resolved to "proxy".\n- "MAP test.com [::1]:77" Forces "test.com" to resolve to IPv6 loopback. Will also force the\nport of the resulting socket address to be 77.\n- "MAP * baz, EXCLUDE www.google.com" Remaps everything to "baz", except for "www.google.com".\n\nThese mappings apply to the endpoint host in a net request (the TCP connect\nand host resolver in a direct connection, and the CONNECT in an HTTP proxy\nconnection, and the endpoint host in a SOCKS proxy connection).'
[[[host_resolver_rules]]]
default_value=''
type='string'
description='Like Host Rules but these rules only apply to the host resolver.'
[[[ppapi_flash_path]]]
display_name='Pepper API'
default_value=''
type='file'
description='Sets the path of the pepper flash plugin.'
[[[ppapi_flash_version]]]
display_name='Pepper API Version'
default_value=''
type='string'
description='Sets the version of the Pepper API field.'
[[[log_net_log]]]
display_name='Net Log Path'
default_value=''
type='file'
exists=False
description='Enables net log events to be saved and writes them to this path.'
[[[disable_renderer_backgrounding]]]
display_name='Disable Renderer Backgrounding'
default_value=False
type='check'
description="Prevents Chromium from lowering the priority of invisible pages'\nrenderer processes. This flag is global to all renderer processes"
[[[ssl_version_fallback_min]]]
display_name='Min SSL Version Fallback'
default_value=''
type='string'
description='Sets the minimum SSL/TLS version ("tls1", "tls1.1" or "tls1.2")\nthat TLS fallback will accept.'
[[[cipher_suite_blacklist]]]
display_name='Cipher Blacklist'
default_value=''
type='string'
description='Specifies comma-separated list of SSL cipher suites to disable.'
[[webkit_settings]]
[[[plugin]]]
@ -117,6 +166,85 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
copy=False
type='file'
[[web_preferences]]
[[[node_integration]]]
type='check'
default_value=True
description='Whether node integration is enabled.'
[[[preload]]]
type='file'
default_value=''
description='Specifies a script that will be loaded before other scripts run in\nthe page. This script will always have access to node APIs no matter whether\nnode integration is turned on or off. When node integration is turned off,\nthe preload script can reintroduce Node global symbols back to the global scope.'
[[[partition]]]
type='string'
description='Sets the session used by the page. If partition starts with "persist:",\nthe page will use a persistent session available to all pages in the app with\nthe same partition. if there is no "persist:" prefix, the page will use\nan in-memory session. By assigning the same partition, multiple pages\ncan share the same session. If the partition is unset then default\nsession of the app will be used.'
[[[zoom_factor]]]
default_value=100
min=1
max=1000
factor=100
label_suffix='%'
type='range'
description='The default zoom factor of the page.'
[[[javascript]]]
type='check'
default_value=True
description='Enables JavaScript support.'
[[[web_security]]]
type='check'
default_value=True
description='When setting false, it will disable the same-origin policy (Usually\nusing testing websites by people), and set allowDisplayingInsecureContent\nand allowRunningInsecureContent to true if these two options are not set by user.'
[[[allow_displaying_insecure_content]]]
type='check'
default_value=False
display_name='Display Insecure'
description='Allow an https page to display content like images from http URLs.'
[[[allow_running_insecure_content]]]
type='check'
default_value=False
display_name='Run Insecure'
description='Allow a https page to run JavaScript, CSS or plugins from http URLs.'
[[[images]]]
type='check'
default_value=True
display_name='Display Images'
description='Enables image support.'
[[[text_areas_are_resizable]]]
type='check'
default_value=True
display_name='Resizable TextAreas'
description='Make TextArea elements resizable.'
[[[webgl]]]
type='check'
default_value=True
description='Enables WebGL support.'
[[[webaudio]]]
type='check'
default_value=True
description='Enables WebAudio support.'
[[[plugins]]]
type='check'
default_value=False
description='Whether plugins should be enabled.'
[[[experimental_features]]]
type='check'
display_name='Exp. Features'
default_value=False
description="Enables Chromium's experimental features."
[[[experimental_canvas_features]]]
type='check'
display_name='Exp. Canvas Features'
default_value=False
description="Enables Chromium's experimental canvas features."
[[[direct_write]]]
type='check'
default_value=True
description='Enables DirectWrite font rendering system on Windows.'
[[[blink_features]]]
type='string'
default_value=''
description='A list of feature strings separated by ",", like "CSSVariables,KeyboardEventKey".\nThe full list of supported feature strings can be found in the\nsetFeatureEnabledFromString function of chromium.'
[[window_settings]]
[[[title]]]
default_value=''
@ -140,11 +268,52 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
file_types='*.png *.jpg *.jpeg'
description='This icon to be displayed for the windows exe of the app. Defaults to Window icon.'
[[[width]]]
default_value='640'
default_value='800'
type='string'
convert='int'
[[[height]]]
default_value='480'
default_value='600'
type='string'
convert='int'
[[[x]]]
default_value='800'
type='string'
convert='int'
description="Window's left offset from screen. Default is to center the window"
[[[y]]]
default_value='600'
convert='int'
type='string'
description="Window's top offset from screen. Default is to center the window."
[[[use_content_size]]]
default_value=False
type='check'
description="The width and height would be used as web page's size, which\nmeans the actual window's size will include window frame's size\nand be slightly larger. Default is false."
[[[accept_first_mouse]]]
type='check'
default_value=False
description='Whether the web view accepts a single mouse-down event that\nsimultaneously activates the window.'
[[[auto_hide_menu_bar]]]
type='check'
default_value=False
description='Auto hide the menu bar unless the Alt key is pressed.'
[[[disable_auto_hide_cursor]]]
type='check'
default_value=False
description='Whether to hide cursor when typing.'
[[[enable_larger_than_screen]]]
display_name='Resize Larger Than Screen'
type='check'
default_value=False
description='Enable the window to be resized larger than screen.'
[[[background_color]]]
type='color'
default_value='#000'
description="Window's background color as Hexadecimal value, like #66CD00 or #FFF.\nThis is only implemented on Linux and Windows."
[[[dark_theme]]]
type='check'
default_value=False
description='Forces using dark theme for the window, only works on some GTK+3\ndesktop environments.'
[[[min_width]]]
default_value=None
type='string'
@ -157,75 +326,67 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
[[[max_height]]]
default_value=None
type='string'
[[[toolbar]]]
display_name='Show Toolbar'
default_value=False
type='check'
description=''
[[[always-on-top]]]
[[[always_on_top]]]
display_name='Keep on Top'
default_value=False
type='check'
description=''
description='Whether the window should always stay on top of other windows.'
[[[frame]]]
display_name='Window Frame'
default_value=True
type='check'
description='Hide the frame of the window'
[[[show_in_taskbar]]]
display_name='Taskbar'
default_value=True
description='Specify false to create a Frameless Window.'
[[[skip_taskbar]]]
display_name='Skip Taskbar'
default_value=False
type='check'
description='Hide the app running in the taskbar'
description='Whether to show the window in the taskbar.'
[[[show]]]
display_name='Show'
default_value=True
type='check'
description='Uncheck to make your app hidden on startup.'
[[[visible]]]
default_value=True
type='check'
description=''
[[[resizable]]]
default_value=False
type='check'
description=''
description='Whether window is resizable.'
[[[fullscreen]]]
default_value=False
type='check'
description=''
[[[position]]]
display_name='Position by'
default_value=None
values=[None, 'mouse', 'center']
type='list'
description='The position to place the window when it opens.'
[[[as_desktop]]]
default_value=False
description='Whether the window should show in fullscreen. When set to false the\nfullscreen button will be hidden or disabled on OS X.'
[[[center]]]
display_name='Center Window'
default_value=True
type='check'
description='Tries to render the app to the desktop background'
description='Show window in the center of the screen.'
[[[transparent]]]
default_value=False
type='check'
description='Allows window tranparency.'
[[[type]]]
display_name='Window Type'
default_value=None
values=[None, 'desktop', 'dock', 'toolbar', 'splash', 'notification', 'textured']
type='list'
description='The type of window to draw. Note that "textured" is Mac OSX only.'
[[[title_bar_style]]]
default_value='default'
values=['default', 'hidden', 'hidden-inset']
type='list'
description='The title bar style. Mac OSX > 10.10 only.'
[[[kiosk]]]
default_value=False
type='check'
description='Puts the application is kiosk mode.'
[[[kiosk_emulation]]]
default_value=False
type='check'
description='Puts the application is kiosk emulation mode. Will automatically check off required settings that will emulate kiosk.'
check_action='set_kiosk_emulation_options'
[[download_settings]]
[[[nw_version]]]
display_name='Node-webkit version'
default_value='0.12.0'
[[[electron_version]]]
display_name='Electron version'
default_value='0.36.7'
values=[]
type='list'
button='Update'
button_callback='update_nw_versions'
button_callback='update_electron_versions'
[[[force_download]]]
default_value=False
type='check'
@ -239,99 +400,37 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
default_value=False
type='check'
url='%(base_url)s%(win_32_dir_prefix)s.zip'
extract_files="""['%(win_32_dir_prefix)s/nw.exe',
'%(win_32_dir_prefix)s/nw.pak',
'%(win_32_dir_prefix)s/icudtl.dat',
'%(win_32_dir_prefix)s/libEGL.dll',
'%(win_32_dir_prefix)s/ffmpegsumo.dll',
'%(win_32_dir_prefix)s/libGLESv2.dll',
'%(win_32_dir_prefix)s/d3dcompiler_46.dll',
'%(win_32_dir_prefix)s/d3dcompiler_47.dll',
'%(win_32_dir_prefix)s/pdf.dll'
]"""
dest_files="""['nw.exe',
'nw.pak',
'icudtl.dat',
'libEGL.dll',
'ffmpegsumo.dll',
'libGLESv2.dll',
'd3dcompiler_46.dll',
'd3dcompiler_47.dll',
'pdf.dll'
]"""
binary_location='electron.exe'
[[windows-x64]]
default_value=False
type='check'
url='%(base_url)s%(win_64_dir_prefix)s.zip'
extract_files="""['%(win_64_dir_prefix)s/nw.exe',
'%(win_64_dir_prefix)s/nw.pak',
'%(win_64_dir_prefix)s/icudtl.dat',
'%(win_64_dir_prefix)s/libEGL.dll',
'%(win_64_dir_prefix)s/ffmpegsumo.dll',
'%(win_64_dir_prefix)s/libGLESv2.dll',
'%(win_64_dir_prefix)s/d3dcompiler_46.dll',
'%(win_64_dir_prefix)s/d3dcompiler_47.dll',
'%(win_64_dir_prefix)s/pdf.dll'
]"""
dest_files="""['nw.exe',
'nw.pak',
'icudtl.dat',
'libEGL.dll',
'ffmpegsumo.dll',
'libGLESv2.dll',
'd3dcompiler_46.dll',
'd3dcompiler_47.dll',
'pdf.dll'
]"""
binary_location='electron.exe'
[[mac-x32]]
default_value=False
type='check'
url='%(base_url)s%(mac_32_dir_prefix)s.zip'
extract_file='%(mac_32_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework'
extract_files="""['%(mac_32_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework',
'%(mac_32_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/nw.pak',
'%(mac_32_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Libraries/ffmpegsumo.so',
'%(mac_32_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/icudtl.dat']"""
dest_files="""['node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework',
'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/nw.pak',
'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Libraries/ffmpegsumo.so',
'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/icudtl.dat']"""
binary_location='Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/Current/Electron Framework'
[[mac-x64]]
default_value=False
type='check'
url='%(base_url)s%(mac_64_dir_prefix)s.zip'
extract_file='%(mac_64_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework'
extract_files="""['%(mac_64_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework',
'%(mac_64_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/nw.pak',
'%(mac_64_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Libraries/ffmpegsumo.so',
'%(mac_64_dir_prefix)s/node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/icudtl.dat']"""
dest_files="""['node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/node-webkit Framework',
'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/nw.pak',
'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Libraries/ffmpegsumo.so',
'node-webkit.app/Contents/Frameworks/node-webkit Framework.framework/Resources/icudtl.dat']"""
binary_location='Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/Current/Electron Framework'
[[linux-x64]]
default_value=False
type='check'
url='%(base_url)s%(linux_64_dir_prefix)s.tar.gz'
extract_file='%(linux_64_dir_prefix)s/nw'
extract_files="""['%(linux_64_dir_prefix)s/nw',
'%(linux_64_dir_prefix)s/nw.pak',
'%(linux_64_dir_prefix)s/libffmpegsumo.so',
'%(linux_64_dir_prefix)s/icudtl.dat']"""
dest_files=['nw', 'nw.pak', 'libffmpegsumo.so', 'icudtl.dat']
url='%(base_url)s%(linux_64_dir_prefix)s.zip'
binary_location='electron'
[[linux-x32]]
default_value=False
type='check'
url='%(base_url)s%(linux_32_dir_prefix)s.tar.gz'
extract_file='%(linux_32_dir_prefix)s/nw'
extract_files="""['%(linux_32_dir_prefix)s/nw',
'%(linux_32_dir_prefix)s/nw.pak',
'%(linux_32_dir_prefix)s/libffmpegsumo.so',
'%(linux_32_dir_prefix)s/icudtl.dat']"""
dest_files=['nw', 'nw.pak', 'libffmpegsumo.so', 'icudtl.dat']
url='%(base_url)s%(linux_32_dir_prefix)s.zip'
binary_location='electron'
[compression]
[[nw_compression_level]]
@ -340,31 +439,43 @@ linux_64_dir_prefix = 'node-webkit-v{}-linux-x64'
min=0
max=9
type='range'
description='Compression to be applied to the executable\'s nwjs binary. 0 is no compression, 9 is maximum. They all use lzma.'
description='Compression to be applied to the executable\'s nwjs binary.\n0 is no compression, 9 is maximum. They all use lzma.'
[[uncompressed_folder]]
display_name='Uncompressed Folder'
type='check'
default_value=False
description='Mac OSX Only! This option makes the resulting app.nw inside the app just a plain folder. This is useful to mitigate startup times and to modify files.'
description='Mac OSX Only! This option makes the resulting app.nw inside\nthe app just a plain folder. This is useful to mitigate\nstartup times and to modify files.'
[order]
application_setting_order="""['main', 'name', 'app_name', 'node-main', 'description', 'version', 'keywords',
'user-agent', 'chromium-args',
'node-remote', 'js-flags', 'inject-js-start', 'inject-js-end',
'additional_trust_anchors', 'snapshot',
'nodejs', 'single-instance', 'plugin', 'java', 'page-cache']"""
window_setting_order = """['title', 'icon', 'mac_icon', 'exe_icon', 'position', 'width', 'height',
'min_width', 'min_height',
'max_width', 'max_height', 'toolbar', 'always-on-top', 'frame',
'show_in_taskbar', 'show', 'visible', 'resizable', 'fullscreen', 'as_desktop',
'kiosk', 'kiosk_emulation', 'transparent']"""
application_setting_order="""['main_html', 'app_name', 'description', 'version',
'user_agent', 'chromium_args', 'js_flags', 'inject_js_start',
'inject_js_end', 'remote_debugging_port', 'client_certificate',
'ignore_connections_limit', 'proxy_server', 'proxy_bypass_list',
'host_resolver_rules', 'log_net_log', 'ppapi_flash_path',
'ppapi_flash_version', 'ssl_version_fallback_min',
'cipher_suite_blacklist',
'ignore_certificate_errors', 'disable_http_cache',
'disable_renderer_backgrounding']"""
window_setting_order = """['title', 'icon', 'mac_icon', 'exe_icon',
'width', 'height', 'x', 'y', 'min_width', 'min_height',
'max_width', 'max_height', 'center', 'use_content_size', 'always_on_top',
'frame', 'skip_taskbar', 'show', 'resizable', 'fullscreen',
'kiosk', 'transparent', 'accept_first_mouse', 'disable_auto_hide_cursor',
'auto_hide_menu_bar', 'enable_larger_than_screen', 'background_color',
'dark_theme', 'type', 'title_bar_style']"""
web_prefs_order = """['preload', 'partition', 'blink_features', 'zoom_factor', 'node_integration',
'javascript', 'web_security', 'allow_displaying_insecure_content',
'allow_running_insecure_content', 'images', 'text_areas_are_resizable',
'webaudio', 'plugins', 'experimental_features', 'experimental_canvas_features',
'direct_write']"""
export_setting_order = """['windows-x32', 'windows-x64', 'mac-x32', 'mac-x64', 'linux-x64', 'linux-x32']"""
compression_setting_order = """['nw_compression_level', 'uncompressed_folder']"""
download_setting_order = """['nw_version', 'download_dir',
download_setting_order = """['electron_version', 'download_dir',
'force_download']"""
[version_info]
urls="""['https://raw.githubusercontent.com/nwjs/nw.js/master/CHANGELOG.md']"""
electron_url='https://api.github.com/repos/atom/electron/releases'

201
main.py
View file

@ -11,7 +11,7 @@ import validators
from PySide import QtGui, QtCore
from PySide.QtGui import QApplication, QHBoxLayout, QVBoxLayout
from PySide.QtNetwork import QHttp
from PySide.QtNetwork import QHttp, QHttpRequestHeader
from PySide.QtCore import QUrl, QFile, QIODevice, QCoreApplication
from pycns import pngs_from_icns
@ -30,6 +30,41 @@ def url_exists(path):
return True
return False
class ColorDisplay(QtGui.QWidget):
colorChanged = QtCore.Signal(QtGui.QColor)
def __init__(self, initial, parent=None):
super(ColorDisplay, self).__init__(parent)
self.color = None
self.setColor(initial, False)
def setColor(self, color, emit=True):
if isinstance(color, (str, bytes)):
self.color = QtGui.QColor(color)
else:
self.color = color
self.update()
if emit:
self.colorChanged.emit(self.color)
def paintEvent(self, event=None):
painter = QtGui.QPainter(self)
if self.color is not None:
painter.setBrush(QtGui.QBrush(self.color))
rect = self.rect()
margins = self.contentsMargins()
new_rect = QtCore.QRect(rect.left()+margins.left(),
rect.top()+margins.top(),
rect.width()-margins.right()*2,
rect.height()-margins.bottom()*2)
painter.drawRect(new_rect)
def getColorName(self):
return str(self.color.name())
class ExistingProjectDialog(QtGui.QDialog):
def __init__(self, recent_projects, directory_callback, parent=None):
super(ExistingProjectDialog, self).__init__(parent)
@ -132,7 +167,7 @@ class BackgroundThread(QtCore.QThread):
class MainWindow(QtGui.QMainWindow, CommandBase):
def update_nw_versions(self, button):
def update_electron_versions(self, button):
self.get_versions_in_background()
def load_recent_projects(self):
@ -252,7 +287,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.setWindowIcon(QtGui.QIcon(get_file('files/images/icon.png')))
self.update_json = False
self.setup_nw_versions()
self.setup_electron_versions()
self.thread = None
self.original_packagejson = {}
@ -266,22 +301,22 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.option_settings_enabled(False)
self.setWindowTitle(u"Web2Executable {}".format(__gui_version__))
self.update_nw_versions(None)
self.update_electron_versions(None)
def open_recent_file(self):
action = self.sender()
if action:
self.load_project(action.data())
def setup_nw_versions(self):
nw_version = self.get_setting('nw_version')
def setup_electron_versions(self):
electron_version = self.get_setting('electron_version')
try:
f = codecs.open(get_data_file_path('files/nw-versions.txt'), encoding='utf-8')
f = codecs.open(get_data_file_path('files/electron-versions.txt'), encoding='utf-8')
for line in f:
nw_version.values.append(line.strip())
electron_version.values.append(line.strip())
f.close()
except IOError:
nw_version.values.append(nw_version.default_value)
electron_version.values.append(electron_version.default_value)
def create_application_layout(self):
self.main_layout = QtGui.QVBoxLayout()
@ -303,12 +338,14 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.comp_settings_widget = self.create_compression_settings()
self.win_settings_widget = self.create_window_settings()
self.ex_settings_widget = self.create_export_settings()
self.web_prefs_widget = self.create_web_prefs_settings()
self.dl_settings_widget = self.create_download_settings()
self.directory_chooser_widget = self.create_directory_choose()
def addWidgets_to_main_layout(self):
self.warning_settings_icon = QtGui.QIcon(get_file('files/images/warning.png'))
self.app_settings_icon = QtGui.QIcon(get_file('files/images/app_settings.png'))
self.web_prefs_icon = QtGui.QIcon(get_file('files/images/web_prefs.png'))
self.win_settings_icon = QtGui.QIcon(get_file('files/images/window_settings.png'))
self.ex_settings_icon = QtGui.QIcon(get_file('files/images/export_settings.png'))
self.comp_settings_icon = QtGui.QIcon(get_file('files/images/compress_settings.png'))
@ -316,22 +353,32 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.tab_icons = [self.app_settings_icon,
self.win_settings_icon,
self.web_prefs_icon,
self.ex_settings_icon,
self.comp_settings_icon,
self.download_settings_icon]
self.main_layout.addWidget(self.directory_chooser_widget)
self.tab_widget.addTab(self.app_settings_widget,
self.app_settings_icon,
'App Settings')
self.tab_widget.addTab(self.win_settings_widget,
self.win_settings_icon,
'Window Settings')
self.tab_widget.addTab(self.web_prefs_widget,
self.web_prefs_icon,
'Web Preferences')
self.tab_widget.addTab(self.ex_settings_widget,
self.ex_settings_icon, 'Export Settings')
self.tab_widget.addTab(self.comp_settings_widget,
self.comp_settings_icon,
'Compression Settings')
self.tab_widget.addTab(self.dl_settings_widget,
self.download_settings_icon,
'Download Settings')
@ -344,6 +391,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.app_settings_widget.setEnabled(is_enabled)
self.win_settings_widget.setEnabled(is_enabled)
self.ex_settings_widget.setEnabled(is_enabled)
self.web_prefs_widget.setEnabled(is_enabled)
self.comp_settings_widget.setEnabled(is_enabled)
self.dl_settings_widget.setEnabled(is_enabled)
self.options_enabled = is_enabled
@ -372,7 +420,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
'the export options!'))
def selected_version(self):
return self.get_setting('nw_version').value
return self.get_setting('electron_version').value
def enable_ui_after_error(self):
self.enable_ui()
@ -395,10 +443,11 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
options_dict = {'app_settings': 0,
'webkit_settings': 0,
'window_settings': 1,
'export_settings': 2,
'web2exe_settings': 2,
'compression': 3,
'download_settings': 4}
'web_preferences': 2,
'export_settings': 3,
'web2exe_settings': 3,
'compression': 4,
'download_settings': 5}
for setting_group_name, setting_group in self._setting_items:
if name in setting_group:
return options_dict.get(setting_group_name, None)
@ -428,7 +477,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.tab_widget.setTabIcon(tab, self.warning_settings_icon)
if (setting.type == 'file' and
setting.value):
setting.value and setting.exists):
setting_path_invalid = not os.path.exists(setting_path)
setting_url_invalid = not url_exists(setting.value)
if setting_path_invalid and setting_url_invalid:
@ -487,9 +536,6 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
def project_dir(self):
return self.project_path
if hasattr(self, 'input_line'):
return self.input_line.text()
return ''
def output_dir(self):
if hasattr(self, 'output_line'):
@ -549,6 +595,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.open_export_button = open_export_button
http = QHttp(self)
http.sslErrors.connect(self.https_error)
http.requestFinished.connect(self.http_request_finished)
http.dataReadProgress.connect(self.update_progress_bar)
http.responseHeaderReceived.connect(self.read_response_header)
@ -557,6 +604,9 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
return hlayout
def https_error(self, errors):
print(errors)
def read_response_header(self, response_header):
# Check for genuine error conditions.
if response_header.statusCode() not in (200, 300, 301, 302, 303, 307):
@ -620,11 +670,11 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.ex_button.setEnabled(self.required_settings_filled())
self.progress_text = 'Done retrieving versions.'
nw_version = self.get_setting('nw_version')
combo = self.find_child_by_name(nw_version.name)
electron_version = self.get_setting('electron_version')
combo = self.find_child_by_name(electron_version.name)
combo.clear()
combo.addItems(nw_version.values)
combo.addItems(electron_version.values)
def make_output_files_in_background(self):
self.ex_button.setEnabled(False)
@ -696,23 +746,15 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
versions = re.findall('v(\d+)\.(\d+)\.(\d+)', path)[0]
minor = int(versions[1])
if minor >= 12:
path = path.replace('node-webkit', 'nwjs')
self.progress_text = u'Downloading {}'.format(path.replace(version_file, ''))
path = self.get_redirected_url(path)
url = QUrl(path)
file_name = setting.save_file_path(self.selected_version(), location)
archive_exists = QFile.exists(file_name)
#dest_files_exist = False
# for dest_file in setting.dest_files:
# dest_file_path = utils.path_join('files', setting.name, dest_file)
# dest_files_exist &= QFile.exists(dest_file_path)
forced = self.get_setting('force_download').value
if archive_exists and not forced:
@ -728,21 +770,17 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
self.enable_ui()
return
mode = QHttp.ConnectionModeHttp
mode = QHttp.ConnectionModeHttps
port = url.port()
if port == -1:
port = 0
self.http.setHost(url.host(), mode, port)
self.http_request_aborted = False
path = QUrl.toPercentEncoding(url.path(), "!$&'()*+,;=:@/")
if path:
path = str(path)
else:
path = u'/'
# Download the file.
self.http_get_id = self.http.get(path, self.out_file)
self.http_get_id = self.http.get(path, to=self.out_file)
def create_icon_box(self, name, text):
style = 'width:48px;height:48px;background-color:white;border-radius:5px;border:1px solid rgb(50,50,50);'
@ -889,7 +927,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
proj_name = os.path.basename(directory)
self.title_label.setText(proj_name)
setting_input = self.find_child_by_name('main')
setting_input = self.find_child_by_name('main_html')
files = (glob.glob(utils.path_join(directory, 'index.html')) +
glob.glob(utils.path_join(directory, 'index.php')) +
glob.glob(utils.path_join(directory, 'index.htm')))
@ -898,13 +936,8 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
setting_input.setText(files[0].replace(self.project_dir() + os.path.sep, ''))
app_name_input = self.find_child_by_name('app_name')
name_input = self.find_child_by_name('name')
name_setting = self.get_setting('name')
title_input = self.find_child_by_name('title')
if not name_input.text():
name_input.setText(name_setting.filter_name(proj_name))
if not app_name_input.text():
app_name_input.setText(proj_name)
@ -948,6 +981,12 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
text_obj.setText(file_path)
setting.last_value = file_path
def get_color(self, obj, color_disp, initial, setting, *args, **kwargs):
color = QtGui.QColorDialog.getColor(QtGui.QColor(initial), obj, 'Choose Color')
if color:
color_disp.setColor(color)
setting.last_value = color.name()
def get_file_reg(self, obj, text_obj, setting, file_types, *args, **kwargs):
file_path, _ = QtGui.QFileDialog.getOpenFileName(self, 'Choose File',
(setting.last_value or
@ -1002,6 +1041,11 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
return self.create_list_setting(name)
elif setting.type == 'range':
return self.create_range_setting(name)
elif setting.type == 'color':
return self.create_color_setting(name)
else:
print('Setting "{}" type "{}" not defined.'.format(name, setting.type))
return QtGui.QHBoxLayout()
def create_window_settings(self):
group_box = QtGui.QWidget()
@ -1010,6 +1054,13 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
group_box.setLayout(vlayout)
return group_box
def create_web_prefs_settings(self):
group_box = QtGui.QWidget()
vlayout = self.create_layout(self.settings['order']['web_prefs_order'], cols=3)
group_box.setLayout(vlayout)
return group_box
def create_export_settings(self):
group_box = QtGui.QWidget()
vlayout = self.create_layout(self.settings['order']['export_setting_order'], cols=4)
@ -1097,8 +1148,8 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
setting_label.setToolTip(setting.description)
setting_label.setStatusTip(setting.description)
glayout.addWidget(setting_label, row, col)
glayout.addLayout(self.create_setting(setting_name),
row, col+1)
sett = self.create_setting(setting_name)
glayout.addLayout(sett, row, col+1)
col += 2
return glayout
@ -1115,7 +1166,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
text.textChanged.connect(self.call_with_object('setting_changed',
text, setting))
if setting.value:
text.setText(setting.value)
text.setText(str(setting.value))
text.setStatusTip(setting.description)
text.setToolTip(setting.description)
@ -1151,6 +1202,37 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
return hlayout
def create_color_setting(self, name):
hlayout = QtGui.QHBoxLayout()
setting = self.get_setting(name)
color_disp = ColorDisplay(setting.value or setting.default_value)
color_disp.setObjectName(setting.name)
color_disp.setContentsMargins(5, 5, 5, 5)
button = QtGui.QPushButton('Choose...')
button.setMaximumWidth(100)
button.setMaximumHeight(26)
button.clicked.connect(self.call_with_object('get_color', button,
color_disp,
setting.value or setting.default_value,
setting))
if setting.value:
color_disp.setColor(setting.value)
color_disp.setStatusTip(setting.description)
color_disp.setToolTip(setting.description)
color_disp.colorChanged.connect(self.call_with_object('setting_changed',
color_disp, setting))
hlayout.addWidget(color_disp)
hlayout.addWidget(button)
return hlayout
def create_text_input_with_folder_setting(self, name):
hlayout = QtGui.QHBoxLayout()
@ -1218,6 +1300,12 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
old_val = setting.default_value
setting.value = old_val
widget.setValue(old_val)
elif setting.type == 'color':
old_val = ''
if setting.default_value is not None:
old_val = setting.default_value
setting.value = old_val
widget.setColor(setting.value)
def set_kiosk_emulation_options(self, is_checked):
if is_checked:
@ -1252,7 +1340,7 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
if (setting.type == 'string' or
setting.type == 'file' or
setting.type == 'folder'):
setting.value = args[0]
setting.value = setting.convert(args[0])
elif setting.type == 'strings':
setting.value = args[0].split(',')
elif setting.type == 'check':
@ -1264,6 +1352,8 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
setting.value = obj.currentText()
elif setting.type == 'range':
setting.value = obj.value()
elif setting.type == 'color':
setting.value = args[0].name()
if setting.action is not None:
action = getattr(self, setting.action, None)
@ -1362,16 +1452,17 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
slider.valueChanged.connect(self.call_with_object('setting_changed',
slider, setting))
slider.setMinimumWidth(60)
slider.setObjectName(setting.name)
slider.setValue(setting.default_value)
slider.setStatusTip(setting.description)
slider.setToolTip(setting.description)
range_label = QtGui.QLabel(str(setting.default_value))
range_label.setMaximumWidth(30)
range_label.setMaximumWidth(45)
slider.valueChanged.connect(self.call_with_object('_update_range_label',
range_label))
range_label, setting))
w = QtGui.QWidget()
whlayout = QtGui.QHBoxLayout()
@ -1383,8 +1474,8 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
return hlayout
def _update_range_label(self, label, value):
label.setText(str(value))
def _update_range_label(self, label, setting, value):
label.setText(str(value)+setting.label_suffix)
def load_package_json(self, json_path=None):
setting_list = super(MainWindow, self).load_package_json(json_path)
@ -1408,6 +1499,8 @@ class MainWindow(QtGui.QMainWindow, CommandBase):
setting_field.setCurrentIndex(index)
if setting.type == 'range':
setting_field.setValue(int(setting.value))
if setting.type == 'color':
setting_field.setColor(setting.value)
self.ex_button.setEnabled(self.required_settings_filled())
def show_and_raise(self):
@ -1425,7 +1518,7 @@ if __name__ == '__main__':
QCoreApplication.setOrganizationName("SimplyPixelated")
QCoreApplication.setOrganizationDomain("simplypixelated.com")
frame = MainWindow(900, 500, app)
frame = MainWindow(1000, 500, app)
frame.show_and_raise()
sys.exit(app.exec_())

View file

@ -19,6 +19,10 @@ def is_windows():
def get_temp_dir():
return tempfile.gettempdir()
def to_camel_case(underscore):
res = ''.join(x.capitalize() or '_' for x in underscore.split('_'))
return res[:1].lower() + res[1:]
def path_join(base, *rest):
new_rest = []
for i in range(len(rest)):