Compare commits

..

No commits in common. 'master' and '0.5.11' have entirely different histories.

  1. 10
      .editorconfig
  2. 21
      .github/CONTRIBUTING.md
  3. 3
      .github/FUNDING.yml
  4. 25
      .github/config.yml
  5. 35
      .github/lock.yml
  6. 14
      .github/no-response.yml
  7. 26
      .github/stale.yml
  8. 21
      .gitignore
  9. 2
      .npmrc
  10. 82
      .sencha/app/Boot.js
  11. 17
      .sencha/app/Microloader.js
  12. 4
      .sencha/app/app.defaults.json
  13. 33
      .sencha/app/build-impl.xml
  14. 74
      .sencha/app/defaults.properties
  15. 65
      .sencha/app/init-impl.xml
  16. 115
      .sencha/app/js-impl.xml
  17. 4
      .sencha/app/native.defaults.properties
  18. 4
      .sencha/app/package.defaults.properties
  19. 62
      .sencha/app/page-impl.xml
  20. 29
      .sencha/app/refresh-impl.xml
  21. 205
      .sencha/app/sass-impl.xml
  22. 3
      .sencha/app/sencha.cfg
  23. 35
      .sencha/app/slice-impl.xml
  24. 59
      .sencha/app/watch-impl.xml
  25. 108
      .travis.yml
  26. 6
      Backers.md
  27. 216
      CONTRIBUTING.md
  28. 282
      README.md
  29. 6
      TODO.md
  30. 53
      app.js
  31. 18
      app.json
  32. 474
      app/Application.js
  33. 29
      app/README.md
  34. 30
      app/Readme.md
  35. 5
      app/model/Readme.md
  36. 2
      app/model/Service.js
  37. 4
      app/model/ServiceList.js
  38. 40
      app/package.json
  39. 0
      app/store/Readme.md
  40. 42
      app/store/Services.js
  41. 834
      app/store/ServicesList.js
  42. 36
      app/util/IconLoader.js
  43. 241
      app/ux/Auth0.js
  44. 420
      app/ux/WebView.js
  45. 35
      app/view/add/Add.js
  46. 1
      app/view/add/AddController.js
  47. 149
      app/view/main/Main.js
  48. 202
      app/view/main/MainController.js
  49. 1
      app/view/main/MainModel.js
  50. 182
      app/view/preferences/Preferences.js
  51. 15
      app/view/preferences/PreferencesController.js
  52. 52
      appveyor.yml
  53. 389
      electron/main.js
  54. 87
      electron/menu.js
  55. 31
      electron/tray.js
  56. 32
      electron/updater.js
  57. 18
      electron/utils/positionOnScreen.js
  58. 4
      env-sample.js
  59. 2
      ext/packages/ext-aria/build/resources/Readme.md
  60. 2
      ext/packages/ext-aria/resources/Readme.md
  61. 4
      ext/packages/ext-aria/sass/etc/Readme.md
  62. 1
      ext/packages/sencha-soap/Readme.md
  63. 63
      index.html
  64. 2
      languages.js
  65. 21
      masterpassword.html
  66. 7220
      package-lock.json
  67. 222
      package.json
  68. 1
      packages/local/rambox-default-theme/Readme.md
  69. 47
      packages/local/rambox-default-theme/examples/Readme.md
  70. 8
      packages/local/rambox-default-theme/sass/Readme.md
  71. 4
      packages/local/rambox-default-theme/sass/etc/Readme.md
  72. 12
      packages/local/rambox-default-theme/sass/etc/all.scss
  73. 4
      packages/local/rambox-default-theme/sass/src/Readme.md
  74. 3
      packages/local/rambox-default-theme/src/Readme.md
  75. 4
      resources/Readme.md
  76. BIN
      resources/auth0.png
  77. BIN
      resources/icons/aim.png
  78. BIN
      resources/icons/airdroid.png
  79. BIN
      resources/icons/allo.png
  80. BIN
      resources/icons/amium.png
  81. BIN
      resources/icons/androidmessages.png
  82. BIN
      resources/icons/aol.png
  83. BIN
      resources/icons/awsworkmail.png
  84. BIN
      resources/icons/bearychat.png
  85. BIN
      resources/icons/bip.png
  86. BIN
      resources/icons/calendar.png
  87. BIN
      resources/icons/ciscospark.png
  88. BIN
      resources/icons/cliq.png
  89. BIN
      resources/icons/clocktweets.png
  90. BIN
      resources/icons/converse.png
  91. BIN
      resources/icons/crisp.png
  92. BIN
      resources/icons/dasher.png
  93. BIN
      resources/icons/devrant.png
  94. BIN
      resources/icons/dingtalk.png
  95. BIN
      resources/icons/discord.png
  96. BIN
      resources/icons/drift.png
  97. BIN
      resources/icons/duo.png
  98. BIN
      resources/icons/element.png
  99. BIN
      resources/icons/facebook.png
  100. BIN
      resources/icons/fastmail.png
  101. Some files were not shown because too many files have changed in this diff Show More

10
.editorconfig

@ -8,13 +8,3 @@ root = true
end_of_line = lf
insert_final_newline = true
indent_style = tab
tab_width = 2
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.{md}]
indent_style = space
indent_size = 4
trim_trailing_whitespace = false

21
.github/CONTRIBUTING.md

@ -1,27 +1,32 @@
# Contributing
## Submitting issues
- Please search the existing issues first, it's likely that your issue was already reported or even fixed.
* Please search the existing issues first, it's likely that your issue was already reported or even fixed.
- Go to "Issues" and type any word in the top search/command bar.
- Consider to search on closed issues. To do that just remove "is:open" from the search field.
- More info on [search syntax within github](https://help.github.com/articles/searching-issues).
- Report the issue using our [template](https://github.com/saenzramiro/rambox/blob/master/.github/ISSUE_TEMPLATE.md), it includes all the information we need to track down the issue.
- More info on [search syntax within github](https://help.github.com/articles/searching-issues)
* Report the issue using our [template][template], it includes all the information we need to track down the issue.
Help us to maximize the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.
[template]: https://github.com/saenzramiro/rambox/blob/master/.github/ISSUE_TEMPLATE.md
## Share your Custom Services
If you consider there are other users that can use your Custom Service, we have a repo for this: [Rambox Services Contrib](https://github.com/saenzramiro/rambox-services-contrib)
If you consider there are other users that can use your Custom Service, we have a repo for this: [Rambox Services Contrib][rambox-services-contrib].
[rambox-services-contrib]: https://github.com/saenzramiro/rambox-services-contrib
## Ask for help
We have a great community in [Slack (Prefered)](https://rambox.typeform.com/to/t7jc4C) or [Gitter](https://gitter.im/saenzramiro/rambox) (Gitter channel is mirroring into Slack `general` room) that can help you with any doubt or problem.
We have a great community in [Gitter][gitter] that can help you with any doubt or problem.
[gitter]: https://gitter.im/saenzramiro/rambox
## Contributing to Source Code
Feel free to create pull requests to help us offer a great and complete software. :wink:
## Translations
Please submit translations via [Transifex][transifex].
Please submit translations via [Crowdin](https://crowdin.com/project/rambox/).
[transifex]: https://www.transifex.com/rambox/rambox-app/

3
.github/FUNDING.yml

@ -1,3 +0,0 @@
# These are supported funding model platforms
custom: ['https://rambox.app/donate.html']

25
.github/config.yml

@ -1,25 +0,0 @@
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
# Comment to be posted to on first time issues
newIssueWelcomeComment: >
Thanks for opening your first issue here! Be sure to follow the issue template!
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
# Comment to be posted to on PRs from first time contributors in your repository
newPRWelcomeComment: >
Thanks for opening this pull request! Please check out our contributing guidelines.
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
# Comment to be posted to on pull requests merged by a first time user
firstPRMergeComment: >
Congrats on merging your first pull request! We here at Rambox are proud of you!
# Configuration for request-info - https://github.com/behaviorbot/request-info
# *Required* Comment to reply with
requestInfoReplyComment: >
We would appreciate it if you could provide us with more info about this issue/pr!
# *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given
requestInfoLabelToAdd: more-information-needed

35
.github/lock.yml

@ -1,35 +0,0 @@
# Configuration for lock-threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 30
# Issues and pull requests with these labels will not be locked. Set to `[]` to disable
exemptLabels:
- archive
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: >
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo

14
.github/no-response.yml

@ -1,14 +0,0 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 14
# Label requiring a response
responseRequiredLabel: more-information-needed
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further.

26
.github/stale.yml

@ -1,26 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels:
- more-information-needed
# Issues with these labels will never be considered stale
exemptLabels:
- bug
- to do
- enhancement
- feature request
- in progress
- investigate
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed since there has not been
any recent activity. Please open a new issue for related bugs.

21
.gitignore vendored

@ -1,10 +1,10 @@
# Sencha (ExtJS)
build/development/
build/production/
build/temp/
/build/development
/build/production
/build/temp
.sass-cache/
.sass_cache/
dist/
/dist
# OS generated files #
######################
@ -18,21 +18,20 @@ Thumbs.db
# IDE generated files #
######################
.project/
.idea/
.project
.idea
# bootstrap files are regenerated by Sencha CMD on every build or refresh
bootstrap.js
bootstrap.css
bootstrap.json
bootstrap.jsonp
# Electron
node_modules/
node_modules
npm-debug.log
# Vagrant
.vagrant/
/.vagrant
env.js
rambox_cfg.json
languages.js
electron/dev-app-update.yml

2
.npmrc

@ -1,2 +0,0 @@
package-lock = false
save-exact = true

82
.sencha/app/Boot.js

@ -537,33 +537,9 @@ Ext.Boot = Ext.Boot || (function (emptyFn) {
init: function () {
var scriptEls = doc.getElementsByTagName('script'),
script = scriptEls[0],
len = scriptEls.length,
re = /\/ext(\-[a-z\-]+)?\.js$/,
entry, src, state, baseUrl, key, n, origin;
// No check for script definedness because there always should be at least one
Boot.hasReadyState = ("readyState" in script);
Boot.hasAsync = ("async" in script);
Boot.hasDefer = ("defer" in script);
Boot.hasOnLoad = ("onload" in script);
// Feature detecting IE
Boot.isIE8 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && !Boot.hasOnLoad;
Boot.isIE9 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
Boot.isIE10p = Boot.hasReadyState && Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
if (Boot.isIE8) {
Boot.isIE10 = false;
Boot.isIE10m = true;
}
else {
Boot.isIE10 = (new Function('/*@cc_on return @_jscript_version @*/')()) === 10;
Boot.isIE10m = Boot.isIE10 || Boot.isIE9 || Boot.isIE8;
}
// IE11 does not support conditional compilation so we detect it by exclusion
Boot.isIE11 = Boot.isIE10p && !Boot.isIE10;
entry, script, src, state, baseUrl, key, n, origin;
// Since we are loading after other scripts, and we needed to gather them
// anyway, we track them in _scripts so we don't have to ask for them all
@ -576,8 +552,12 @@ Ext.Boot = Ext.Boot || (function (emptyFn) {
state = script.readyState || null;
// If we find a script file called "ext-*.js", then the base path is that file's base path.
if (!baseUrl && re.test(src)) {
baseUrl = src;
if (!baseUrl) {
if (re.test(src)) {
Boot.hasReadyState = ("readyState" in script);
Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;
baseUrl = src;
}
}
if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {
@ -598,6 +578,8 @@ Ext.Boot = Ext.Boot || (function (emptyFn) {
if (!baseUrl) {
script = scriptEls[scriptEls.length - 1];
baseUrl = script.src;
Boot.hasReadyState = ('readyState' in script);
Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;
}
Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
@ -1412,36 +1394,27 @@ Ext.Boot = Ext.Boot || (function (emptyFn) {
createLoadElement: function(callback) {
var me = this,
el = me.getElement();
me.preserve = true;
el.onerror = function() {
me.error = true;
if (callback) {
callback();
callback = null;
}
};
if (Boot.isIE10m) {
el.onreadystatechange = function() {
el = me.getElement(),
readyStateChange = function(){
if (this.readyState === 'loaded' || this.readyState === 'complete') {
if (callback) {
if(callback) {
callback();
callback = this.onreadystatechange = this.onerror = null;
}
}
},
errorFn = function() {
me.error = true;
if(callback) {
callback();
}
};
me.preserve = true;
el.onerror = errorFn;
if(Boot.hasReadyState) {
el.onreadystatechange = readyStateChange;
} else {
el.onload = callback;
}
else {
el.onload = function() {
callback();
callback = this.onload = this.onerror = null;
};
}
// IE starts loading here
el[me.prop] = me.getLoadUrl();
},
@ -1568,11 +1541,8 @@ Ext.Boot = Ext.Boot || (function (emptyFn) {
// for async modes, we have some options
if (!sync) {
// if cross domain, just inject the script tag and let the onload
// events drive the progression.
// IE10 also needs sequential loading because of a bug that makes it
// fire readystate event prematurely:
// https://connect.microsoft.com/IE/feedback/details/729164/ie10-dynamic-script-element-fires-loaded-readystate-prematurely
if (Boot.isIE10 || me.isCrossDomain()) {
// events drive the progression
if(me.isCrossDomain()) {
return me.loadCrossDomain();
}
// for IE, use the readyStateChange allows us to load scripts in parallel

17
.sencha/app/Microloader.js

@ -51,24 +51,15 @@ Ext.Microloader = Ext.Microloader || (function () {
? manifest
: manifest + ".json";
if (location.href.indexOf('file:/') === 0) {
Boot.load(url + 'p');
}
else {
Boot.fetch(url, function(result){
Microloader.setManifest(JSON.parse(result.content));
});
}
Boot.fetch(url, function(result){
manifest = Ext.manifest = JSON.parse(result.content);
Microloader.load(manifest);
});
} else {
Microloader.load(manifest);
}
},
setManifest: function(cfg) {
manifest = Ext.manifest = cfg;
Microloader.load(manifest);
},
/**
*
* @param manifestDef

4
.sencha/app/app.defaults.json

@ -135,7 +135,7 @@
*/
"production": {
"compressor": {
"type": "cmd"
"type": "yui"
}
},
@ -170,7 +170,7 @@
* to adjust the base path for all bootstrap objects, or expanded into object form:
*
* "bootstrap": {
* "base": "${app.dir}",
* "base": "${app.dir},
* "manifest": "bootstrap.json",
* "microloader": "bootstrap.js",
* "css": "bootstrap.css"

33
.sencha/app/build-impl.xml

@ -244,15 +244,9 @@ this file in most cases.
<target name="watch"
depends="-watch-init,development,init"
description="Starts Watch to keep your app ready for dev mode">
<local name="watch.lock.file"/>
<condition property="watch.lock.file" value="${build.id}.watch.lock">
<isset property="build.id"/>
</condition>
<property name="watch.lock.file" value="app.watch.lock"/>
<x-ant-call>
<param name="build.id" value="${build.id}"/>
<param name="build.name" value="${build.name}"/>
<param name="watch.lock.file" value="${build.dir}/${watch.lock.file}"/>
<target name="-before-watch"/>
<target name="-watch"/>
<target name="-after-watch"/>
@ -469,33 +463,6 @@ this file in most cases.
depends="init,-before-publish,-publish,-after-publish"
description="Publish app to Sencha Web Application Manager"/>
<!--
===============================================================
Build Dependencies
uses the compiler to build metadata files for all detected
file-to-file dependencies
===============================================================
-->
<target name="build-dependencies" depends="init, -detect-app-build-properties">
<x-compile refid="${compiler.ref.id}">
<![CDATA[
restore
page
and
meta
-infoType=Dependencies
-basePath=${build.dir}
-tpl={0}
-out=${build.dir}/dependencies.json
and
meta
-infoType=AppManifest
-basePath=${build.dir}
-tpl={0}
-out=${build.dir}/bootsequence.json
]]>
</x-compile>
</target>
<!--
===============================================================

74
.sencha/app/defaults.properties

@ -1,7 +1,7 @@
# =============================================================================
# This file defines properties used by build-impl.xml and the associated
# *-impl.xml files (sass-impl.xml, js-impl.xml, etc.), which are the core of
# the applications build process.
# the applications build process.
#
# This file represents the lowest priority file for defining these properties
# as well as the place to look for documentation and learning what properties
@ -48,7 +48,7 @@
# See "sencha help app build" for details.
#
# The corresponding properties files:
# (production.properties, testing.properties, etc.) provide examples of
# (production.properties, testing.properties, etc.) provide examples of
# overriding sets of properties depending on the selected environment
# NOTE: this replaces the deprecated args.environment
app.environment=production
@ -120,8 +120,6 @@ app.output.cache.path=${app.output.cache}
app.output.cache.enable=true
app.output.appCache.enable=${app.output.cache.enable}
app.output.appCache.path=${app.output.cache.path}
app.output.progressive=false
app.output.progressive.enable=${app.output.progressive}
build.out.base.path=${app.output.base}
build.out.page.path=${build.out.base.path}/${app.output.page.path}
@ -238,13 +236,13 @@ build.options=logger:${build.options.logger},debug:${build.options.debug},produc
# This property can be modified to change general build options
# such as excluding files from the set. The format expects newlines
# for each argument, for example:
#
#
# build.operations=\
# exclude\n \
# -namespace=Ext\n
#
# NOTE: modifications to build.operations are intended to be
# placed in an override of the "-after-init" target, where it
# NOTE: modifications to build.operations are intended to be
# placed in an override of the "-after-init" target, where it
# can be calculated based on other
# ant properties
#
@ -270,7 +268,7 @@ build.optimize.enable=\
build.optimize.disable=
build.optimize=${build.optimize.disable}
# enables / disables removing text references from
# enables / disables removing text references from
# package js build files
build.remove.references=true
@ -282,12 +280,12 @@ build.remove.requirement.nodes=true
# like mixin references
build.optimize.string.references=true
# enables / disables cmd compression
build.compression.cmd=${app.output.js.compress}
# enables / disables yui compression
build.compression.yui=${app.output.js.compress}
# enables / disables closure compression
build.compression.closure=0
# enables / disables uglify compression
build.compression.ugilfy=0
@ -298,8 +296,8 @@ build.compile.temp.dir.keep=true
# ------------------------------------------
# DOC ONLY - Do Not Set
# this variable will be set to the appropriate compressor
# option, and is calculated in init-impl.xml, but may be overridded in
# this variable will be set to the appropriate compressor
# option, and is calculated in init-impl.xml, but may be overridded in
# app.properties, <environment>.properties, or via command line
#
# build.compression=
@ -314,8 +312,8 @@ build.compile.temp.dir.keep=true
# markup, or left as a separate resource
build.enable.embedded.microloader=${app.output.microloader.embed}
# whether to include the page's manifest.json code with the
# microloader content. Production.properties files should set this to
# whether to include the page's manifest.json code with the
# microloader content. Production.properties files should set this to
# false to have manifest.json exist as a server resource.
build.enable.embedded.manifest=${app.output.manifest.embed}
@ -350,18 +348,18 @@ app.microloader=${app.microloader.dir}/${app.microloader.name}
app.microloader.path=${app.microloader}
# specifies how to embed the microloader code into the output markup
# {0} is replaced with the content of the microloader file specified
# {0} is replaced with the content of the microloader file specified
# by app.microloader.path
build.microloader.code.tpl={0}
# the template to use when generating a stand-alone json manifest file
build.microloader.json.tpl.standalone={0}
# the template to use when embedding the manifest json directly next to the
# the template to use when embedding the manifest json directly next to the
# microloader in the output microloader content
build.microloader.json.tpl.embedded=Ext.blink({0});
# the template to use in the output microloader content when supplying
# the template to use in the output microloader content when supplying
# the manifest json as a separate server-side resource ('production' builds)
build.microloader.json.tpl.external=Ext.blink('{'id:''${app.id}'''}');
@ -370,7 +368,7 @@ build.microloader.json.tpl.external=Ext.blink('{'id:''${app.id}'''}');
build.embedded.microloader.tpl=<script id="microloader" data-app="${app.id}" type="text/javascript">{0}</script>
# the compressor to use when embedding the microloader into a page
# can be -closure or -cmd, or leave empty to disable compression
# can be -closure or -yui, or leave empty to disable compression
build.embedded.microloader.compressor=
# the path to the microloader content file, if external to the outpout markup
@ -385,7 +383,7 @@ build.external.microloader.markup=<script id="microloader" data-app="${app.id}"
# currently unused : is a placeholder for future microloader interactions
build.microloader.mode=${build.environment}
# the tag name to use when generating the compiler save set for
# the tag name to use when generating the compiler save set for
# the page's js code
build.tag.name=full-page
@ -487,7 +485,7 @@ enable.ext42.themes=false
enable.touch.themes=false
# --------------------
# selector count threshold to use when
# selector count threshold to use when
# splitting a single css file into multiple
# css files (IE selector limit workaround)
#
@ -499,7 +497,7 @@ build.css.selector.limit=${app.output.css.split}
build.css.preprocess=${app.output.css.preprocess}
# sets the css preprocessor options, in the form:
# name1:value1,name2:value2,...
# name1:value1,name2:value2,...
build.css.preprocessor.opts=
# enables / disable css compressor (enable.ext42.themes only)
@ -582,32 +580,32 @@ theme.name=default
# *****************************************************************************
# the resources directory of the application
# note: this property is currently only used for building ext 4.1 style themes
# note: this property is currently only used for building ext 4.1 style themes
# (used by x-build-theme and x-copy-resources in slice-impl.xml)
app.resources.dir=${app.dir}/resources
# the directory containing the slicer widget example page
app.example.dir=${app.dir}/sass/example
# this is the directory used for intermediate build artifacts used
# by the slicer for generating theme images
app.example.build.dir=${build.temp.dir}/slicer-temp
# properties to control the recirect css file that is
# generated for the slicer example page
app.example.css.name=example.css
app.example.css.file=${app.example.build.dir}/${app.example.css.name}
app.example.css.file=${app.example.dir}/${app.example.css.name}
# the base path for generating the bootstrap code for the
# the base path for generating the bootstrap code for the
# slicer page
bootstrap.base.path=${app.example.build.dir}
bootstrap.base.path=${app.example.dir}
# the full file name of the slicer page's bootstrap js file
bootstrap.example.js=${app.example.build.dir}/bootstrap.js
bootstrap.example.js=${app.example.dir}/bootstrap.js
# the full file name of the slicer page's bootstrap js file
bootstrap.example.json.name=bootstrap.json
bootstrap.example.json=${app.example.build.dir}/${bootstrap.example.json.name}
bootstrap.example.json=${app.example.dir}/${bootstrap.example.json.name}
# this is the directory used for intermediate build artifacts used
# by the slicer for generating theme images
app.example.build.dir=${build.temp.dir}/slicer-temp
# the name of the intermediate screenshot file used for image slicing
build.capture.png=${app.example.build.dir}/theme-capture.png
@ -617,8 +615,8 @@ build.capture.json=${app.example.build.dir}/theme-capture.json
# the location of the slicer widget page
app.example.theme.html.name=theme.html
app.example.theme.html=${cmd.dir}/ant/build/slicer/${app.example.theme.html.name}
app.example.fashion.html.name=fashion.html
app.example.theme.html=${app.example.dir}/${app.example.theme.html.name}
app.example.fashion.html=${app.example.dir}/${app.example.fashion.html.name}
# a name prefix used for slicer page temporary artifacts
@ -646,7 +644,7 @@ build.slice.options=
# *****************************************************************************
# Packager
# these properties control features of the native packaging phase of the
# these properties control features of the native packaging phase of the
# build process
# *****************************************************************************
@ -657,13 +655,13 @@ enable.desktop.packager=false
# skips packaging the built application with cordova/phonegap
skip.native-package=false
# a property that controls whether a standalone manifest.json file will be
# a property that controls whether a standalone manifest.json file will be
# generated for the native packaged application
enable.standalone.manifest=false
# *****************************************************************************
# Resolve
# these properties control aspects of the dynamic dependency resolver, which
# these properties control aspects of the dynamic dependency resolver, which
# uses phantomjs to load the application and extract Ext.Loader class load
# history.
# *****************************************************************************
@ -693,7 +691,7 @@ build.resolve.mode=references
# the output file for the detected dynamic dependencies
build.resolve.file=${build.temp.dir}/resolve.json
# controls whether unmatched external references in the specified file will
# controls whether unmatched external references in the specified file will
# generate build warnings or build failures
build.resolve.allow.unmatched=true
@ -706,4 +704,4 @@ build.resolve.allow.unmatched=true
build.trigger.targets=refresh,resources,sass
# the watcher targets to run that monitor for code changes
build.watcher.targets=-watch-fashion,-watch-compiler
build.watcher.targets=-watch-compiler

65
.sencha/app/init-impl.xml

@ -19,13 +19,11 @@
<script language="javascript">
<![CDATA[
var f = new java.io.File(project.getProperty("basedir"));
var sub = ".sencha/workspace/sencha.cfg"
var sub2 = "workspace.json";
var sub = ".sencha/workspace/sencha.cfg";
for (var p = f; p; p = p.getParentFile()) {
var t = new java.io.File(p, sub);
var t2 = new java.io.File(p, sub2);
if (t.exists() || t2.exists()) {
if (t.exists()) {
// we found the workspace folder!
t = new java.io.File(p, "local.properties");
@ -89,20 +87,13 @@
<!--
calculate the appropriate build.compression value
-->
<condition property="build.compression" value="-cmd">
<condition property="build.compression" value="-yui">
<or>
<x-is-true value="${build.compression.yui}"/>
<equals arg1="yui" arg2="${app.compressor.type}"/>
</or>
</condition>
<condition property="build.compression" value="-cmd">
<or>
<x-is-true value="${build.compression.cmd}"/>
<equals arg1="cmd" arg2="${app.compressor.type}"/>
</or>
</condition>
<condition property="build.compression" value="-closure">
<or>
<x-is-true value="${build.compression.closure}"/>
@ -153,8 +144,6 @@
<property name="app.sass.fashion" value="false"/>
<property name="app.sass.rhino" value="false"/>
<property name="app.sass.dynamic" value="false"/>
<property name="app.sass.generated.var" value="${app.sass.save}"/>
</target>
<target name="-after-init"/>
@ -230,23 +219,6 @@
</else>
</if>
<if>
<or>
<equals arg1="${app.output.js.filter}" arg2="all"/>
<equals arg1="${app.output.js.filter}" arg2="minimum"/>
</or>
<then>
<property name="enable.used.deps" value="false"/>
</then>
</if>
<if>
<equals arg1="${app.output.js.filter}" arg2="used"/>
<then>
<property name="enable.used.deps" value="true"/>
</then>
</if>
<if>
<equals arg1="${app.toolkit}" arg2="modern"/>
<then>
@ -267,18 +239,6 @@
</then>
</if>
<if>
<or>
<x-is-false value="${app.output.progressive.enable}"/>
<not>
<isset property="${app.output.progressive.enable}"/>
</not>
</or>
<then>
<property name="${skip.progressive}" value="1"/>
</then>
</if>
<if>
<!--If Deltas are FALSE, deltas do not exist, or caching is disabled then skip delta patching-->
<or>
@ -380,19 +340,6 @@
</condition>
<property name="enable.split.framework" value="false"/>
<if>
<equals arg1="${app.output.js.filter}" arg2="used"/>
<then>
<property name="include.used.package.deps">
include
-usedPackagesDeps
and
</property>
</then>
</if>
<property name="include.used.package.deps">
# no-op
</property>
<x-compile refid="${compiler.ref.id}"
dir="${app.dir}"
@ -425,7 +372,6 @@
${build.operations}
and
${exclude.boot}
${include.used.package.deps}
save
page
]]>
@ -445,7 +391,6 @@
${build.operations}
and
${exclude.boot}
${include.used.package.deps}
save
page
]]>
@ -458,9 +403,7 @@
defaultSassFile="${app.out.scss}"
defaultCssFile="${app.out.css}"
refid="app.web.server"
saveVariablesProp="app.sass.generated.var"
uiDirProp="app.sass.generated.src"
sassNamespaceProp="app.sass.namespace"
saveVariablesProp="app.sass.save"
j2eeMode="${use.webxml}">
<mapping name="~cmd" path="${cmd.dir}"/>
<mapping name="" path="${build.web.root}"/>

115
.sencha/app/js-impl.xml

@ -10,121 +10,18 @@
splitModePropName="enable.split.mode"
pageModePropName="app.page.mode"
hasJsSdkPropName="app.has.js.sdk"
hasCssSdkPropName="app.has.css.sdk"
hasUsesPackagesPropName="app.has.uses" />
hasCssSdkPropName="app.has.css.sdk"/>
</target>
<!--
this is the standard js compile target that builds the output js file(s)
-->
<target name="-compile-js" depends="-detect-app-build-properties">
<property name="app.output.framework.include"
value="package-sencha-core,framework,toolkit,package-core"/>
<property name="enable.used.deps" value="${app.has.uses}"/>
<if>
<x-is-true value="${enable.used.deps}"/>
<then>
<x-compile refid="${compiler.ref.id}">
# determine the base set of framework files
exclude
-all
and
include
-tag=${app.output.framework.include}
and
save
allframework
</x-compile>
<for param="file">
<fileset dir="${workspace.build.dir}/temp" includes="**/deps.json"/>
<sequential>
<x-compile refid="${compiler.ref.id}">
# now load the transitive set based on the json data
exclude
-all
and
include
-json-data=@{file}
-r
and
save
deps
and
# add any needed deps to the page save set
include
-set=page
and
save
page
</x-compile>
</sequential>
</for>
</then>
<else>
<if>
<equals arg1="${app.output.js.filter}" arg2="all"/>
<then>
<x-compile refid="${compiler.ref.id}">
# if filtering requirements is configured for 'all'
# then just include all js content on the classpath to
# ensure all dependencies are provided for the used packages
include
-all
and
save
page
</x-compile>
</then>
</if>
</else>
</if>
<if>
<x-is-true value="${enable.split.mode}"/>
<then>
<local name="build.js.framework.fwset" />
<local name="build.js.framework.rtl" />
<if>
<or>
<x-is-true value="${app.output.framework.all}" />
<isset property="app.has.uses" />
</or>
<then>
<property name="build.include.all.scss" value="true" />
<property name="build.js.framework.fwset"> </property>
<if>
<x-is-false value="${app.output.framework.rtl}" />
<then>
<property name="build.js.framework.rtl">
<![CDATA[
exclude
-namespace=Ext.rtl
and
]]>
</property>
</then>
<else>
<property name="build.js.framework.rtl"> </property>
</else>
</if>
</then>
<else>
<property name="build.js.framework.fwset">
<![CDATA[
intersect
-set=page,allframework
and
]]>
</property>
<property name="build.js.framework.rtl"> </property>
</else>
</if>
<property name="app.output.framework.include"
value="package-sencha-core,framework,toolkit,package-core"/>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
@ -143,11 +40,12 @@
save
allframework
and
${build.js.framework.fwset}
intersect
-set=page,allframework
and
save
frameworkdeps
and
${build.js.framework.rtl}
include
-tag=Ext.cmd.derive
and
@ -182,7 +80,6 @@
</x-compile>
</then>
<else>
<local name="framework.include.filter"/>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
# build an all-classes.js file that contains

4
.sencha/app/native.defaults.properties

@ -18,8 +18,8 @@ build.options.logger=no
build.options.debug=false
# enable cmd compression
build.compression.cmd=1
# enable yui compression
build.compression.yui=1
enable.standalone.manifest=true

4
.sencha/app/package.defaults.properties

@ -21,7 +21,7 @@ build.options.logger=no
build.options.debug=false
# enable cmd compression
build.compression.cmd=1
# enable yui compression
build.compression.yui=1
app.microloader.name=testing.js

62
.sencha/app/page-impl.xml

@ -2,22 +2,15 @@
<macrodef name="x-build-microload-markup">
<sequential>
<if>
<not>
<equals arg1="${build.compression}" arg2=""/>
</not>
<then>
<x-sencha-command dir="${app.dir}" inheritall="true">
<![CDATA[
fs
minify
${build.embedded.microloader.compressor}
-from=${build.microloader.path}
-to=${build.microloader.path}
]]>
</x-sencha-command>
</then>
</if>
<x-sencha-command dir="${app.dir}" inheritall="true">
<![CDATA[
fs
minify
${build.embedded.microloader.compressor}
-from=${build.microloader.path}
-to=${build.microloader.path}
]]>
</x-sencha-command>
<if>
<x-is-true value="${build.enable.embedded.microloader}"/>
<then>
@ -62,7 +55,6 @@
# generate json file
microload
-operation=manifest
-jsonp=Ext.Microloader.setManifest
-fashion=${use.fashion}
-tpl=${build.microloader.json.tpl.embedded}
-out=${build.microloader.path}
@ -85,7 +77,6 @@
# generate json file
microload
-operation=manifest
-jsonp=Ext.Microloader.setManifest
-fashion=${use.fashion}
-tpl=${build.microloader.json.tpl.standalone}
-out=${build.out.json.path}
@ -316,44 +307,11 @@
value="&lt;html"/>
</target>
<target name="-generate-progressive-webapp" depends="-init-compiler">
<if>
<x-is-false value="${skip.progressive}"/>
<then>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
progressive-webapp
-buildDir=${build.out.base.path}
]]>
</x-compile>
</then>
</if>
</target>
<target name="-generate-service-worker" depends="-init-compiler">
<if>
<x-is-false value="${skip.progressive}"/>
<then>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
progressive-webapp
-buildDir=${build.out.base.path}
+serviceWorker
]]>
</x-compile>
</then>
</if>
</target>
<target name="-before-page"/>
<target name="-page"
depends="-copy-app-resources,
-generate-progressive-webapp,
-generate-deltas,
-build-output-page,
-generate-cache-manifest,
-generate-service-worker"/>
-generate-cache-manifest"/>
<target name="-after-page"/>
</project>

29
.sencha/app/refresh-impl.xml

@ -19,9 +19,6 @@
<not>
<isset property="app.watch.enabled"/>
</not>
<not>
<isset property="app.uses"/>
</not>
</and>
</condition>
<property name="manifest.root.excludes" value=""/>
@ -44,9 +41,6 @@
exclude
-tag=${refresh.file.filter}
and
exclude
-file=Boot.js
and
save
bootstrap
]]>
@ -58,9 +52,6 @@
include
-all
and
exclude
-file=Boot.js
and
save
bootstrap
]]>
@ -72,9 +63,9 @@
to="${build.json.bootstrap.path}"
property="build.json.bootstrap.rel.path"/>
<x-file-write file="${app.bootstrap.js}">var Ext = Ext || {};
<echo file="${app.bootstrap.js}">var Ext = Ext || {};
Ext.manifest = Ext.manifest || "${build.json.bootstrap.rel.path}";
</x-file-write>
</echo>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
@ -87,7 +78,6 @@ Ext.manifest = Ext.manifest || "${build.json.bootstrap.rel.path}";
and
microload
-operation=manifest
-jsonp=Ext.Microloader.setManifest
-fashion=${use.fashion}
-bootstrap
+ignoreDisabled
@ -114,13 +104,13 @@ Ext.manifest = Ext.manifest || "${build.json.bootstrap.rel.path}";
by the default development.js microloader
-->
<x-file-write file="${build.json.bootstrap.path}">
<echo file="${build.json.bootstrap.path}">
/**
* This file is generated by Sencha Cmd and should NOT be edited. It is a
* combination of content from app.json, and all required package's package.json
* files. Customizations should be placed in app.json.
*/
</x-file-write>
</echo>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
microload
@ -139,14 +129,9 @@ Ext.manifest = Ext.manifest || "${build.json.bootstrap.rel.path}";
'-detect-app-build-properties' is defined in js-impl.xml
-->
<target name="-refresh-app"
depends="-detect-app-build-properties">
<if>
<equals arg1="development" arg2="${build.environment}"/>
<then>
<x-ant-call target="-refresh-app-manifest,-refresh-app-bootstrap"/>
</then>
</if>
</target>
depends="-detect-app-build-properties,
-refresh-app-manifest,
-refresh-app-bootstrap"/>
<!--
Refresh app

205
.sencha/app/sass-impl.xml

@ -15,10 +15,7 @@
property="image.search.path"/>
<if>
<or>
<x-is-true value="${app.has.uses}" />
<x-is-true value="${build.include.all.scss}"/>
</or>
<x-is-true value="${build.include.all.scss}"/>
<then>
<property name="sass.name.filter">
include
@ -110,21 +107,18 @@
<copy file="${app.out.scss.tmp}" tofile="${app.out.scss}" overwrite="true"/>
</then>
</if>
<if>
<equals arg1="development" arg2="${build.environment}"/>
<then>
<!--
app.out.css.path is relative to the app output index.html file
-->
<x-get-relative-path
from="${app.dir}"
to="${app.out.css}"
property="app.out.css.path"
/>
<!--
app.out.css.path is relative to the app output index.html file
-->
<x-get-relative-path
from="${app.dir}"
to="${app.out.css}"
property="app.out.css.path"
/>
<!--update the application's bootstrap.css file to point to the build output-->
<x-file-write file="${build.bootstrap.css.path}">
<echo file="${build.bootstrap.css.path}">
<![CDATA[
/*
* This file is generated by Sencha Cmd and should NOT be edited. It redirects
@ -133,9 +127,7 @@
*/
@import '${app.out.css.path}';
]]>
</x-file-write>
</then>
</if>
</echo>
</target>
<!--
@ -194,130 +186,73 @@
scss file, then running compass with the css, sass, and config options set
-->
<target name="-compass-compile-theme-package" depends="-load-sass-page">
<if>
<x-is-true value="${skip.sass.rebuild}"/>
<then>
<x-run-if-true value="${enable.ext42.themes}">
<x-ant-call target="-compile-sass"/>
</x-run-if-true>
</then>
<else>
<x-run-if-true value="${enable.ext42.themes}">
<local name="compress.uptodate"/>
<x-ant-call target="-compile-sass"/>
<x-run-if-true value="${enable.ext42.themes}">
<local name="compress.uptodate"/>
<x-ant-call target="-compile-sass"/>
<if>
<x-is-true value="${app.sass.fashion}"/>
<then>
<if>
<x-is-true value="${app.sass.fashion}"/>
<x-is-true value="${app.sass.rhino}"/>
<then>
<if>
<x-is-true value="${app.sass.rhino}"/>
<then>
<x-fashion-compile
file="${app.out.scss}"
toFile="${app.out.css}"/>
</then>
<else>
<x-fashion-live-update input="${app.out.scss}"
output="${app.out.css}"
refId="app.web.server"
split="${build.css.selector.limit}"
compress="${build.css.compress}"
compilerRefId="${compiler.ref.id}"/>
<x-compile refid="${compiler.ref.id}">
<![CDATA[
microload
-operation=manifest
-fashion=false
-tpl=${build.microloader.json.tpl.standalone}
-out=${build.out.json.path}
-resourcePath=${build.out.base.path}
-basePath=${build.out.metadata.dir}
]]>
</x-compile>
<x-sencha-command>
fashion
-config=${build.out.json.path}
-compress=${build.css.compress}
-split=${build.css.selector.limit}
-saveFile=${app.dir}/${app.sass.generated.var}
-slicer=false
${app.out.scss}
${app.out.css}
</x-sencha-command>
<x-update-css-array input="${app.out.css}"
compilerRefId="${compiler.ref.id}"/>
<if>
<and>
<available file="${build.out.css.dir}/css-vars.js"/>
</and>
<then>
<if>
<not>
<equals arg1="${build.compression}" arg2=""/>
</not>
<then>
<x-compress-js srcFile="${build.out.css.dir}/css-vars.js"
outFile="${build.out.css.dir}/css-vars.js"/>
</then>
</if>
<if>
<not>
<equals arg1="${build.environment}" arg2="development"/>
</not>
<then>
<concat destfile="${build.out.js.path}" append="true">
<fileset dir="${build.out.css.dir}">
<include name="css-vars.js"/>
</fileset>
</concat>
</then>
</if>
</then>
</if>
</else>
</if>
<x-fashion-compile
file="${app.out.scss}"
toFile="${app.out.css}"/>
</then>
<else>
<x-compass-compile
rubyPath="${build.ruby.path}"
dir="${compass.working.dir}"
trace="${compass.compile.trace}"
boring="${compass.compile.boring}"
force="${compass.compile.force}"
sassdir="${compass.sass.dir}"
cssdir="${compass.css.dir}"
config="${compass.config.file}"/>
<x-fashion-live-update input="${app.out.scss}"
output="${app.out.css}"
refId="app.web.server"
split="${build.css.selector.limit}"
compress="${build.css.compress}"
compilerRefId="${compiler.ref.id}"/>
<uptodate property="compress.uptodate"
value="true"
srcfile="${app.out.scss}.tmp"
targetfile="${app.out.css}"/>
<x-sencha-command>
fashion
-compress=${build.css.compress}
-split=${build.css.selector.limit}
-saveFile=${app.dir}/${app.sass.save}
${app.out.scss}
${app.out.css}
</x-sencha-command>
<if>
<x-is-true value="${compress.uptodate}"/>
<!--<x-is-true value="true"/>-->
<then>
<x-compress-css-files dir="${build.out.css.dir}"
prefix="${app.out.base.debug}"
outprefix="${app.out.base}"
preprocess="${build.css.preprocess}"
compress="${build.css.compress}"/>
</then>
</if>
<x-update-css-array input="${app.out.css}"
compilerRefId="${compiler.ref.id}"/>
</else>
</if>
</x-run-if-true>
</else>
</if>
</then>
<else>
<x-compass-compile
rubyPath="${build.ruby.path}"
dir="${compass.working.dir}"
trace="${compass.compile.trace}"
boring="${compass.compile.boring}"
force="${compass.compile.force}"
sassdir="${compass.sass.dir}"
cssdir="${compass.css.dir}"
config="${compass.config.file}"/>
<uptodate property="compress.uptodate"
value="true"
srcfile="${app.out.scss}.tmp"
targetfile="${app.out.css}"/>
<if>
<x-is-true value="${compress.uptodate}"/>
<!--<x-is-true value="true"/>-->
<then>
<x-compress-css-files dir="${build.out.css.dir}"
prefix="${app.out.base.debug}"
outprefix="${app.out.base}"
preprocess="${build.css.preprocess}"
compress="${build.css.compress}"/>
</then>
</if>
</else>
</if>
</x-run-if-true>
</target>
<!--

3
.sencha/app/sencha.cfg

@ -42,5 +42,4 @@ app.resource.paths=${app.dir}/resources
app.framework.version=5.1.1.451
app.cmd.version=6.6.0.13
app.cmd.version=6.1.2.15

35
.sencha/app/slice-impl.xml

@ -40,7 +40,6 @@
-all
and
sass
+skipWatch
+class-name-vars
+etc
+vars
@ -55,7 +54,6 @@
page
and
sass
+skipWatch
+ruby
-output=${app.example.out.ruby}
]]>
@ -70,7 +68,7 @@
</then>
</if>
<x-get-relative-path from="${app.example.build.dir}"
<x-get-relative-path from="${app.example.dir}"
to="${app.example.css}"
property="app.example.css.path"/>
@ -99,15 +97,12 @@
<x-is-true value="${app.sass.rhino}"/>
<then>
<x-fashion-compile
slicer="true"
file="${app.example.build.dir}"
toFile="${app.example.build.dir}"/>
</then>
<else>
<x-sencha-command>
fashion
-config=${app.dir}/app.json
-slicer=true
-compress=${build.css.compress}
-split=${build.css.selector.limit}
${app.example.build.dir}
@ -132,22 +127,10 @@
<!-- Produces a bootstrap.js file for ext 4.2 slicer pages -->
<target name="-generate-slicer-bootstrap" unless="framework.isV5">
<local name="relpath"/>
<local name="cmd.dir.normalized"/>
<x-get-relative-path from="${bootstrap.base.path}"
to="${framework.packages.dir}"
property="relpath"/>
<script language="javascript">
<![CDATA[
var dir = project.getProperty("cmd.dir") + '';
dir = dir.replace(/\\/g, '/');
if (dir.indexOf('/') !== 0) {
dir = '/' + dir;
}
project.setProperty("cmd.dir.normalized", dir);
]]>
</script>
<x-bootstrap file="${bootstrap.example.js}"
basedir="${bootstrap.base.path}"
includeBoot="true"
@ -157,7 +140,7 @@
overrideExcludeTags="">
<![CDATA[
Ext.Boot.loadSync([
"${cmd.dir.normalized}/ant/build/slicer/render.js",
"render.js",
"${relpath}/ext-theme-base/sass/example/manifest.js",
"${relpath}/ext-theme-base/sass/example/shortcuts.js",
"custom.js"
@ -199,7 +182,6 @@ Ext.Boot.loadSync([
<![CDATA[
slicer-manifest
-exclude=${manifest.root.excludes}
-jsonp=Ext.Microloader.setManifest
-removeBootstrapCssEntries=${remove.slicer.css.bootstrap.entries}
+ignoreDisabled
-basePath=${bootstrap.base.path}
@ -219,13 +201,12 @@ Ext.Boot.loadSync([
<echo>Capture theme image to ${build.capture.png}</echo>
<x-sencha-command>
<![CDATA[
theme
capture
-base=${app.example.build.dir}
-page=${app.example.theme.html}
-image=${build.capture.png}
-manifest=${build.capture.json}
]]>
theme
capture
-page=${app.example.theme.html}
-image=${build.capture.png}
-manifest=${build.capture.json}
]]>
</x-sencha-command>
</target>

59
.sencha/app/watch-impl.xml

@ -27,24 +27,6 @@
fork="true"/>
</target>
<target name="-watch-fashion" if="framework.isV6">
<x-fashion-watch
refName="fashion-watch"
inputFile="${app.out.scss}"
outputFile="${app.out.css}"
split="${build.css.selector.limit}"
compress="${build.css.compress}"
configFile="${build.out.json.path}"
saveFile="${app.dir}/${app.sass.generated.var}"
fork="true"/>
</target>
<target name="-stop-fashion-watch">
<x-fashion-watch
refName="fashion-watch"
stop="true"/>
</target>
<macrodef name="x-run-compass-watch">
<attribute name="directory"/>
<sequential>
@ -76,35 +58,18 @@
<target name="-before-watch"/>
<target name="-watch" depends="-init-web-server">
<local name="skip.sass.rebuild"/>
<condition property="skip.sass.rebuild" value="true">
<isset property="framework.isV6"/>
</condition>
<property name="skip.sass.rebuild" value="false"/>
<if>
<x-lock-file file="${watch.lock.file}" refId="lock.ref"/>
<then>
<x-ant-call target="${build.trigger.targets}"/>
<x-ant-call target="web-start" unless="skip.web.start">
<param name="enable.background.server" value="true"/>
</x-ant-call>
<x-ant-call target="${build.watcher.targets}">
<param name="build.id" value="${build.id}"/>
<param name="build.name" value="${build.name}"/>
<param name="skip.sass.rebuild" value="${skip.sass.rebuild}"/>
</x-ant-call>
<x-ant-call target="web-stop" unless="skip.web.start">
<param name="enable.background.server" value="true"/>
</x-ant-call>
<x-ant-call target="-stop-compass-watch"/>
<x-ant-call target="-stop-fashion-watch"/>
<x-unlock-file refId="lock.ref"/>
</then>
<else>
<echo level="error"
message="App watch is already running for this build profile."/>
</else>
</if>
<x-ant-call target="${build.trigger.targets}"/>
<x-ant-call target="web-start" unless="skip.web.start">
<param name="enable.background.server" value="true"/>
</x-ant-call>
<x-ant-call target="${build.watcher.targets}">
<param name="build.id" value="${build.id}"/>
<param name="build.name" value="${build.name}"/>
</x-ant-call>
<x-ant-call target="web-stop" unless="skip.web.start">
<param name="enable.background.server" value="true"/>
</x-ant-call>
<x-ant-call target="-stop-compass-watch"/>
</target>
<target name="-after-watch" depends="init"/>
</project>

108
.travis.yml

@ -1,49 +1,81 @@
matrix:
include:
- os: osx
osx_image: xcode12.4
language: node_js
node_js: "14.16.1"
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
sudo: required
language: node_js
node_js:
- "4"
- os: linux
sudo: required
services: docker
language: generic
branches:
only:
- master
- /\d+\.\d+\.\d+/
os:
- linux
- osx
cache:
directories:
- node_modules
- $HOME/.cache/electron
- $HOME/.cache/electron-builder
- node_modules
- $HOME/.electron
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_install:
- |
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v2.3.1/git-lfs-$([ "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-2.3.1.tar.gz | tar -xz -C /tmp/git-lfs --strip-components 1
export PATH="/tmp/git-lfs:$PATH"
# native dependencies
- if [ $TRAVIS_OS_NAME == "linux" ]; then
sudo apt-get update -qq;
sudo apt-get install -qq libxml2-dev;
sudo apt-get install -qq libappindicator1;
sudo apt-get install -qq rpm;
fi
install:
- git config --global core.autocrlf input
- git reset --hard HEAD
- npm run sencha:clean
- git clone https://github.com/saenzramiro/rambox-build.git $TRAVIS_BUILD_DIR/build/production/Rambox/
before_script:
- git lfs pull
- npm install
- npm uninstall electron-prebuilt
- npm i electron@$ELECTRON
- npm --prefix $TRAVIS_BUILD_DIR/build/production/Rambox/ install $TRAVIS_BUILD_DIR/build/production/Rambox/
script:
- |
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
docker run --rm -ti \
--env-file <(env | grep -vE '\r|\n' | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') \
-v ${PWD}/build/production/Rambox/:/project \
-v ~/.cache/electron:/root/.cache/electron \
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
electronuserland/builder \
/bin/bash -c "npm i && npm run build:linux"
else
cd $TRAVIS_BUILD_DIR/build/production/Rambox/
npm i
npm i cli-truncate
npm run build:osx
fi
- echo $TRAVIS_OS_NAME
- node --version
- npm --version
- npm run build:$TRAVIS_OS_NAME
deploy:
provider: releases
api_key:
secure: F7z4j9xzhpVyfovrf33jbVdW9dwgpIgbn85dxe8y/kZbQ9sAVU8LzoBZ3PlfeXmoV5CnSUa5j/fe8BmPKiG31kgAH7cktsCOgyOLggO1Sy+vnx7RQwwE/jzb7PzEqI2BP6TG/eOfylBxGBInWeaoSRfK3IRAvLHvvxY1eL4Wq/KRaanzMvh+JdCaStIKJSnI/rc9noI7vtOtgHot+F162s1G1k5/UcdbsfXWzotMVsonhlGgB/Jz9srnJpMpxYxaknR8UFPVRzOM5UzzMo64hXRUDnCFlYcTHcSAkvgKHI0SIPIS3kOdvgrYGavPLK2CAteQk7Dy1lHnEixmKPxG7eVAFwleUo8ePR82WXFkv5gacCvcrGrRLQ9nC/ygeMVgC5XxcHq3ykkcrstu5yjC1yEYFs23gA1Z4DIctwq1diyO72/FNxghV5ckN5WtJQs1QTT+Ec2NjLrWgFYuaEHYgAGopzTPN8gT9fyIv/lAq1S/l4P/z3EDS42UL+UTM77gyXSYShCKneDnFC34ATlYNDhJFRzLZ6qJG5UlOwrfXxUYeGUpQZlV41jg+VFWmPDAeQTMjGP5fRA3w1hFobBotBVyrtbcovTb4TFVTKJWOC/0wyiwku7YTY/nqMU/hR4IluTyRh9Z4NMn5xT5UW63OZd4DJvE573JxmJmnPc0fbo=
file_glob: true
file:
- "dist/*.zip"
- "dist/*.deb"
- "dist/*.tar.gz"
- "dist/*.AppImage"
- "dist/*.rpm"
- "dist/mac/*.zip"
- "dist/mac/*.dmg"
skip_cleanup: true
overwrite: true
prerelease: true
on:
repo: saenzramiro/rambox
tags: true
all_branches: true
notifications:
email:
recipients:
- rambox@protonmail.com
on_success: always
on_failure: always
webhooks:
urls:
- https://webhooks.gitter.im/e/0f214eb0d0017d3c5561
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # options: [always|never|change] default: always

6
Backers.md

@ -0,0 +1,6 @@
# Monthly Donators
[Martin Grünbaum](https://github.com/alathon)
Ivan Toshkov
[Simon Joda Stößer](https://github.com/SimJoSt)

216
CONTRIBUTING.md

@ -1,216 +0,0 @@
# Contributor's Guide
We welcome pull requests! Follow these steps to contribute:
1. Find an [issue](https://github.com/ramboxapp/community-edition/issues) that needs assistance.
1. Let us know you are working on it by posting a comment on the issue.
1. Follow the [Contribution Guidelines](#contribution-guidelines) to start working on the issue.
Working on your first Pull Request? You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)
##### If you've found a bug that is not on the board, [follow these steps](README.md#found-a-bug).
---
## Contribution Guidelines
### Setup
- [Prerequisites](#prerequisites)
- [Forking the Project](#forking-the-project)
- [Create a Branch](#create-a-branch)
- [Set Up rambox](#set-up-rambox)
### Create
- [Make Changes](#make-changes)
- [Run The Test Suite](#run-the-test-suite)
### Submit
- [Creating a Pull Request](#creating-a-pull-request)
- [Common Steps](#common-steps)
- [How We Review and Merge Pull Requests](#how-we-review-and-merge-pull-requests)
- [How We Close Stale Issues](#how-we-close-stale-issues)
- [Next Steps](#next-steps)
- [Other Resources](#other-resources)
### Prerequisites
| Prerequisite | Version |
| ------------------------------------------------------------- | ------- |
| [Sencha](https://www.sencha.com/products/extjs/cmd-download/) | `=6.6.0.13` |
| [Ruby](https://www.ruby-lang.org/en/downloads/) | `=2.3` |
| [Node.js](https://nodejs.org) | `~ ^4.0.0` |
| npm (comes with Node) | `~ ^3.8.7` |
> _Updating to the latest releases is recommended_.
If Node.js, ruby, or sencha cmd is already installed on your machine, run the following commands to validate the versions:
```shell
node -v
ruby -v
sencha
```
If your versions are lower than the prerequisite versions, you should update.
### Forking the Project
#### Setting Up Your System
1. Install [Git](https://git-scm.com/) or your favorite Git client.
1. (Optional) [Setup an SSH Key](https://help.github.com/articles/generating-an-ssh-key/) for GitHub.
#### Forking rambox
1. Go to the top level rambox repository: <https://github.com/ramboxapp/community-edition>
1. Click the "Fork" Button in the upper right hand corner of the interface ([More Details Here](https://help.github.com/articles/fork-a-repo/))
1. After the repository (repo) has been forked, you will be taken to your copy of the rambox repo at <https://github.com/yourUsername/community-edition>
#### Cloning Your Fork
1. Open a Terminal / Command Line / Bash Shell in your projects directory (_i.e.: `/yourprojectdirectory/`_)
1. Clone your fork of rambox
- `git clone https://github.com/yourUsername/community-edition.git`
**(make sure to replace `yourUsername` with your GitHub username)**
This will download the entire rambox repo to your projects directory.
#### Setup Your Upstream
1. Change directory to the new rambox directory (`cd community-edition`)
1. Add a remote to the official rambox repo:
- `git remote add upstream https://github.com/ramboxapp/community-edition.git`
Congratulations, you now have a local copy of the rambox repo! :tada:
### Create a Branch
Before you start working, you will need to create a separate branch specific to the issue / feature you're working on. You will push your work to this branch.
#### Naming Your Branch
Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature you are attempting to add. For example
`fix/email-login` would be a branch where you fix something specific to email login.
#### Adding Your Branch
To create a branch on your local machine (and switch to this branch):
```shell
git checkout -b [name_of_your_new_branch]
```
and to push to GitHub:
```shell
git push origin [name_of_your_new_branch]
```
**If you need more help with branching, take a look at [this](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches).**
### Set Up rambox
Once you have rambox cloned, before you start the application, you first need to install all of the dependencies:
```bash
# Install NPM dependencies
npm install
```
```bash
# Compile the files...
sencha app watch
npm start # in a new terminal
```
### Make Changes
This bit is up to you!
#### How to find the code in the rambox codebase to fix/edit
The best way to find out any code you wish to change/add or remove is using the GitHub search bar at the top of the repository page.
For example, you could search for a challenge name and the results will display all the files along with line numbers.
Then you can proceed to the files and verify this is the area that you were looking forward to edit.
Always feel free to reach out to the chat room when you are not certain of any thing specific in the code.
#### Adding or Editing Services
The services are stored inside the file `./api/services.json` in the `gh-pages` branch:
https://github.com/ramboxapp/community-edition/blob/gh-pages/api/services.json
Add your service to the *BOTTOM* of the array.
To see these changes you'll need to stop your `npm start` and `sencha app watch`, and then rerun those.
### Creating a Pull Request
#### What is a Pull Request?
A pull request (PR) is a method of submitting proposed changes to the rambox repo (or any repo, for that matter).
You will make changes to copies of thefiles which make up rambox in a personal fork, then apply to have them accepted by rambox proper.
#### Important: ALWAYS EDIT ON A BRANCH
Take away only one thing from this document: Never, **EVER** make edits to the `master` branch.
ALWAYS make a new branch BEFORE you edit files.
This is critical, because your copy of `master` will be forever sullied and the only way to fix it is a difficult hard-reset and force-push process, or deleting your fork and re-forking.
### Common Steps
1. Once the edits have been committed, you will be prompted to create a pull request on your fork's GitHub Page.
1. By default, all pull requests should be against the rambox main repo, `master` branch.
- **Make sure that your Base Fork is set to ramboxapp/community-edition when raising a Pull Request.**
1. Submit a pull request.
1. The title (also called the subject) of your PR should be descriptive of your changes and succinctly indicates what is being fixed.
- **Do not add the issue number in the PR title or commit message.**
- Examples: `Added Service servicename` `Correct typo in menu`
1. In the body of your PR include a more detailed summary of the changes you made and why.
- If the PR is meant to fix an existing bug/issue then, at the end of your PR's description, append the keyword `closes` and #xxxx (where xxxx is the issue number).
- Example: `closes #1337`. This tells GitHub to automatically close the existing issue, if the PR is merged.
1. Indicate if you have tested on a local copy of the site or not.
### How We Review and Merge Pull Requests
Rambox has a team of volunteer Issue Moderators. These Issue Moderators routinely go through open pull requests in a process called [Quality Assurance](https://en.wikipedia.org/wiki/Quality_assurance) (QA).
1. If an Issue Moderator QA's a pull request and confirms that the new code does what it is supposed without seeming to introduce any new bugs, they will comment: `"LGTM" which means "Looks good to me."`
1. Another Issue Moderator will QA the same pull request.
- Once they have also confirmed that the new code does what it is supposed to without seeming to introduce any new bugs, they will merge the pull request.
If you would like to apply to join our Issue Moderator team - which is a Core Team position - message [@BerkeleyTrue](https://gitter.im/berkeleytrue) with links to 5 of your pull requests that have been accepted and 5 issues where you have helped someone else through commenting or QA'ing.
### How We Close Stale Issues
We will close any issues or pull requests that have been inactive for more than 15 days, except those that match the following criteria:
- Bugs that are confirmed
- Pull requests that are waiting on other pull requests to be merged
- Features that are a part of a GitHub project
### Next Steps
#### If your PR is accepted
Once your PR is accepted, you may delete the branch you created to submit it.
This keeps your working fork clean.
You can do this with a press of a button on the GitHub PR interface. You can delete the local copy of the branch with: `git branch -D branch/to-delete-name`
#### If your PR is rejected
Don't despair! You should receive solid feedback as to why it was rejected and what changes are needed.
Many Pull Requests, especially first Pull Requests, require correction or updating.
If you have used the GitHub interface to create your PR, you will need to close your PR, create a new branch, and re-submit.
If you have a local copy of the repo, you can make the requested changes and amend your commit with: `git commit --amend`
This will update your existing commit.
When you push it to your fork you will need to do a force push to overwrite your old commit: `git push --force`
Be sure to post in the PR conversation that you have made the requested changes.

282
README.md

@ -1,72 +1,139 @@
# EOL
The Community Edition of Rambox is no longer maintained. We highly recommend that you update to the new version of Rambox, which has a FREE plan with all the features you already use and much more!
Visit: https://rambox.app/download
Thank you so much for all the users who contribute to this project all these years.
[Read our blog post to know more about our new Rambox.](https://rambox.medium.com/hello-2022-hello-new-rambox-4bdef5d6c3b8)
---
<div align="center">
<h1>
<br />
<a href="https://rambox.pro"><img src="./resources/Icon.png" width="256px" alt="Rambox" /></a><br />
Rambox CE
<br /><br/>
</h1>
<h4>Free, Open Source and Cross Platform messaging and emailing app that combines common web applications into one.</h4>
<p>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WU75QWS7LH2CA" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate with PayPal" /></a>
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"><img src="https://img.shields.io/github/license/saenzramiro/rambox.svg" alt="GNU GPL v3" /></a>
<a href="https://gitter.im/saenzramiro/rambox" target="_blank"><img src="https://badges.gitter.im/saenzramiro/rambox.svg" alt="Gitter" /></a>
<a href="https://github.com/saenzramiro/rambox/releases/latest" target="_blank">
<img src="https://img.shields.io/github/release/saenzramiro/rambox.svg" alt="Release" />
</a>
<a target="_blank" href="https://crowdin.com/project/rambox"><img src="https://d322cqt584bo4o.cloudfront.net/rambox/localized.svg" /></a>
</p>
<p>
<a href="https://travis-ci.org/saenzramiro/rambox" target="_blank"><img src="https://travis-ci.org/saenzramiro/rambox.svg?branch=master" alt="Travis CI" /></a>
<a href="https://ci.appveyor.com/project/saenzramiro/rambox" target="_blank"><img src="https://ci.appveyor.com/api/projects/status/3kk9ixjgxwrh7yfy?svg=true" alt="AppVeyor CI" /></a>
<a href="https://david-dm.org/saenzramiro/rambox" title="Dependency status"><img src="https://david-dm.org/saenzramiro/rambox.svg" /></a>
<a href="https://david-dm.org/saenzramiro/rambox#info=devDependencies" title="devDependency status"><img src="https://david-dm.org/saenzramiro/rambox/dev-status.svg" /></a>
</p>
<h5>Available for Windows, Mac and Linux.</h5>
<h5><a href="https://rambox.pro/#ce" target="_blank"><img src="https://cdn.rawgit.com/saenzramiro/rambox/gh-pages/images/img-download.svg" width="250" alt="DOWNLOAD HERE" /></a></h5>
<h6>Logo designed by <a href="https://www.linkedin.com/in/andriyyurchenko/" target="_blank">Andriy Yurchenko</a></h6>
</div>
---
## Table of Contents
- [Screenshot](#screenshot)
- [Apps available](#apps-available)
- [Features](#features)
- [Privacy](#privacy)
- [Donations](#donations)
- [Translations](#translations)
- [Install on Linux - Steps](#install-on-linux---steps)
- [Contributing](#contributing)
- [Quickstart](#quickstart)
- [Disclosure](#disclosure)
- [Licence](#licence)
---
<h1 align="center">
<br>
<a href="http://rambox.pro"><img src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/Icon.png" width="256px" alt="Rambox"></a>
<br>
Rambox
<br>
<br>
</h1>
<h4 align="center">Free, Open Source and Cross Platform messaging and emailing app that combines common web applications into one.</h4>
<p align="center">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WU75QWS7LH2CA" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate with PayPal"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"><img src="https://img.shields.io/github/license/saenzramiro/rambox.svg" alt="GNU GPL v3"></a>
<a href="https://gitter.im/saenzramiro/rambox" target="_blank"><img src="https://badges.gitter.im/saenzramiro/rambox.svg" alt="Gitter"></a>
<a href="https://github.com/saenzramiro/rambox/releases/latest" target="_blank"><img src="https://img.shields.io/github/release/saenzramiro/rambox.svg" alt="Release"></a>
<a target="_blank" href="https://crowdin.com/project/rambox"><img src="https://d322cqt584bo4o.cloudfront.net/rambox/localized.svg"></a>
</p>
<p align="center">
<a href="https://travis-ci.org/saenzramiro/rambox" target="_blank"><img src="https://travis-ci.org/saenzramiro/rambox.svg?branch=master" alt="Travis CI"></a>
<a href="https://ci.appveyor.com/project/saenzramiro/rambox" target="_blank"><img src="https://ci.appveyor.com/api/projects/status/3kk9ixjgxwrh7yfy?svg=true" alt="AppVeyor CI"></a>
<a href="https://david-dm.org/saenzramiro/rambox" title="Dependency status"><img src="https://david-dm.org/saenzramiro/rambox.svg"/></a>
<a href="https://david-dm.org/saenzramiro/rambox#info=devDependencies" title="devDependency status"><img src="https://david-dm.org/saenzramiro/rambox/dev-status.svg"/></a>
</p>
<h5 align="center">Available for Windows, Mac and Linux.</h5>
<h5 align="center"><a href="http://rambox.pro/#download" target="_blank"><img src="https://cdn.rawgit.com/saenzramiro/rambox/gh-pages/images/img-download.svg" width="250" alt="DOWNLOAD HERE"></a></h5>
<h6 align="center">Logo designed by <a href="http://andyur.com/" target="_blank">Andriy Yurchenko</a></h6>
----------
## Screenshot
![Rambox](./resources/screenshots/mac.png)
## Apps available
Visit our website https://rambox.app/#apps and select the "Community-Edition" filter to see all the apps available.
![Rambox](https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/screenshots/mac.png)
## Services available - 89
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/whatsapp.png" alt="WhatsApp" title="WhatsApp">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/messenger.png" alt="Messenger" title="Messenger">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/skype.png" alt="Skype" title="Skype">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/slack.png" alt="Slack" title="Slack">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hangouts.png" alt="Hangouts" title="Hangouts">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/telegram.png" alt="Telegram" title="Telegram">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/wechat.png" alt="WeChat" title="WeChat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/gmail.png" alt="Gmail" title="Gmail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/inbox.png" alt="Inbox" title="Inbox">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hipchat.png" alt="HipChat" title="HipChat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/allo.png" alt="Allo" title="Allo">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/chatwork.png" alt="ChatWork" title="ChatWork">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/groupme.png" alt="GroupMe" title="GroupMe">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/grape.png" alt="Grape" title="Grape">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/gitter.png" alt="Gitter" title="Gitter">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/steam.png" alt="Steam" title="Steam">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/discord.png" alt="Discord" title="Discord">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/noysi.png" alt="Noysi" title="Noysi">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/outlook.png" alt="Outlook" title="Outlook">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/outlook365.png" alt="Outlook 365" title="Outlook 365">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/tutanota.png" alt="TutaNota" title="TutaNota">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hushmail.png" alt="Hushmail" title="Hushmail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/bearychat.png" alt="BearyChat" title="BearyChat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/aol.png" alt="Aol" title="Aol">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/sync.png" alt="Sync" title="Sync">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/aim.png" alt="Aim" title="Aim">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/wire.png" alt="Wire" title="Wire">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/rocketchat.png" alt="Rocket Chat" title="Rocket Chat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/missive.png" alt="Missive" title="Missive">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/yahoo.png" alt="Yahoo! Mail" title="Yahoo! Mail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/ryver.png" alt="Ryver" title="Ryver">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/yandex.png" alt="Yandex Mail" title="Yandex Mail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/dasher.png" alt="Dasher" title="Dasher">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/dingtalk.png" alt="DingTalk" title="DingTalk">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/flowdock.png" alt="FlowDock" title="FlowDock">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/mattermost.png" alt="Mattermost" title="Mattermost">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/voxer.png" alt="Voxer" title="Voxer">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/glip.png" alt="Glip" title="Glip">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/yahoomessenger.png" alt="Yahoo! Messenger" title="Yahoo! Messenger">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/mysms.png" alt="mysms" title="mysms">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/icq.png" alt="ICQ" title="ICQ">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/tweetdeck.png" alt="TweetDeck" title="TweetDeck">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zinc.png" alt="Zinc" title="Zinc">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/freenode.png" alt="FreeNode" title="FreeNode">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/mightytext.png" alt="MightyText" title="MightyText">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zohoemail.png" alt="Zoho Email" title="Zoho Email">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zohochat.png" alt="Zoho Chat" title="Zoho Chat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/roundcube.png" alt="Roundcube" title="Roundcube">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/horde.png" alt="Horde" title="Horde">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/squirrelmail.png" alt="SquirrelMail" title="SquirrelMail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zimbra.png" alt="Zimbra" title="Zimbra">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hootsuite.png" alt="Hootsuite" title="Hootsuite">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/amium.png" alt="Amium" title="Amium">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/rainloop.png" alt="RainLoop" title="RainLoop">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/icloud.png" alt="iCloud Mail" title="iCloud Mail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/irccloud.png" alt="IRC Cloud" title="IRC Cloud">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/kiwi.png" alt="Kiwi IRC" title="Kiwi IRC">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/smooch.png" alt="Smooch" title="Smooch">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/crisp.png" alt="Crisp" title="Crisp">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/flock.png" alt="Flock" title="Flock">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/openmailbox.png" alt="Openmailbox" title="Openmailbox">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/typetalk.png" alt="Typetalk" title="Typetalk">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/drift.png" alt="Drift" title="Drift">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/spark.png" alt="Cisco Spark" title="Cisco Spark">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/fleep.png" alt="Fleep" title="Fleep">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/socialcast.png" alt="Socialcast" title="Socialcast">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/riot.png" alt="Riot" title="Riot">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/pushbullet.png" alt="Pushbullet" title="Pushbullet">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/movim.png" alt="Movim" title="Movim">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/kaiwa.png" alt="Kaiwa" title="Kaiwa">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zyptonite.png" alt="Zyptonite" title="Zyptonite">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/linkedin.png" alt="LinkedIn" title="LinkedIn">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/lounge.png" alt="The Lounge" title="The Lounge">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/kezmo.png" alt="Kezmo" title="Kezmo">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/teams.png" alt="Teams" title="Teams">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/xing.png" alt="Xing" title="Xing">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/workplace.png" alt="Workplace" title="Workplace">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/fastmail.png" alt="FastMail" title="FastMail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hibox.png" alt="Hibox" title="Hibox">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/jandi.png" alt="Jandi" title="Jandi">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/threema.png" alt="Threema" title="Threema">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/messengerpages.png" alt="Messenger for Pages" title="Messenger for Pages">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/vk.png" alt="VK Messenger" title="VK Messenger">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/mastodon.png" alt="Mastodon" title="Mastodon">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/teamworkchat.png" alt="Teamwork Chat" title="Teamwork Chat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/clocktweets.png" alt="ClockTweets" title="ClockTweets">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/intercom.png" alt="Intercom" title="Intercom">
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
## Features
@ -85,55 +152,82 @@ Visit our website https://rambox.app/#apps and select the "Community-Edition" fi
- [x] Custom Code Injection.
- [x] Keyboard Shortcuts.
- [x] Proxy.
- [x] Switch from horizontal to vertical tab bar.
## Privacy
No personal information will be saved
Sessions will persist using the [partition:persist](https://electronjs.org/docs/api/webview-tag#partition) attribute for Webviews.
So every time you open Rambox, your sessions will keep alive until you remove the service.
Sessions will persist using the [partition:persist](http://electron.atom.io/docs/api/web-view-tag/#partition) attribute for Webviews. So every time you open Rambox, your sessions will keep alive until you remove the service.
Sync feature use Auth0 for Single Sign On & Token Based Authentication and to store the services that user is using (and the configuration for each service).
You are always welcome to check the code! ;)
Sync feature use Auth0 for Single Sign On & Token Based Authentication and to store the services that user is using (and the configuration for each service). You are always welcome to check the code! ;)
## Donations
| Type | URL/Wallet |
| ---------------- | :----------------------------------------------------------------------------------------: |
| Credit Cards | [HERE](https://rambox.app/donate.html) |
| Cryptocurrencies | [HERE](https://www.vaulty.io/v/b6480279-af28-4855-868c-17e5cb0ae7fa) |
| Type | URL/Wallet |
|----------|:------------------------------------------------------------------------------------------:|
| PayPal | [HERE](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WU75QWS7LH2CA) |
| Bitcoin | 1Jpa6aSqLjeAiwEzDEBiYcR7dcy5YGYdmB |
| Ethereum | 0xFa20A0CEc2888fcB1aA8d15D85Af2B7E472b874F |
| Litecoin | Lc5L7mkH5L5U3TR7i8mAQpCu6jUm6J1F4C |
## Translations
Help us translate Rambox on <https://crowdin.com/project/rambox/invite>.
Help us translate Rambox on https://crowdin.com/project/rambox/invite.
## [Install on Linux - Steps](https://github.com/saenzramiro/rambox/wiki/Install-on-Linux)
## [To Do](https://github.com/saenzramiro/rambox/blob/master/TODO.md)
## Getting Involved
Want to report a bug, request a feature, contribute to or translate Rambox? We need all the help we can get! Fork and work!
## Getting Started
If you're comfortable getting up and running from a `git clone`, this method is for you.
#### Technologies:
* Sencha Ext JS 5.1.1.451
* Electron
* Node JS
#### Environment:
## [Install on Linux - Steps](https://github.com/ramboxapp/community-edition/wiki/Install-on-Linux)
* Sencha Cmd 6.1.2.15 (make sure to check "Compass extension" during install if you don't have installed yet)
* Ruby 2.3
* NPM 3.8.7
* Node.js 4.0.0
## [Contributing](./CONTRIBUTING.md)
#### Quickstart:
Want to report a bug, request a feature, contribute to or translate Rambox?
We need all the help we can get!
Fork and work!
1. `git clone https://github.com/saenzramiro/rambox.git`
2. `npm install`
3. Configure `env-sample.js` and rename it to `env.js`.
4. `npm run sencha:compile`
5. `npm start`
### Quickstart
#### Compile on Ubuntu:
```shell
git clone https://github.com/saenzramiro/rambox.git
cd rambox
npm install
sencha app watch
npm start
```
These instructions were tested with Ubuntu 17.04.
1. Install dependencies: `sudo apt install nodejs-legacy npm git`
2. Build and install electron: `sudo npm install electron-prebuilt -g`
3. Install Sencha Cmd (non-free): https://www.sencha.com/products/extjs/cmd-download/
4. Clone repository: `git clone https://github.com/saenzramiro/rambox.git`
5. Install npm dependencies: `npm install`
6. Configure `env-sample.js` and rename it to `env.js`.
7. Compile: `npm run sencha:compile`
8. Start program: `npm start`
See [Contributing.md](./CONTRIBUTING.md) for more detailed information about getting set up.
---
-------------------
## Disclosure
Disclosure
-------------------
Rambox is not affiliated with any of the messaging apps offered.
## Licence
Licence
-------------------
[GNU GPL v3](https://github.com/ramboxapp/community-edition/blob/master/LICENSE)
[GNU GPL v3](https://github.com/saenzramiro/rambox/LICENSE)

6
TODO.md

@ -0,0 +1,6 @@
## To Do
- Change theme.
- Deeplink to add new service.
- Dock Menu (http://electron.atom.io/docs/tutorial/desktop-environment-integration/#custom-dock-menu-os-x)
- Crash Reporter.

53
app.js

@ -15,23 +15,12 @@ Ext.application({
// auto update logic
const ipc = require('electron').ipcRenderer;
const { ContextMenuBuilder, ContextMenuListener } = require('electron-contextmenu-wrapper');
const contextMenuBuilder = new ContextMenuBuilder();
const contextMenuListener = new ContextMenuListener(function(event, info) {
contextMenuBuilder.showPopupMenu(info);
});
ipc.on('showAbout', function(event, message) {
!Ext.cq1('about') ? Ext.create('Rambox.view.main.About') : '';
});
ipc.on('showPreferences', function(event, message) {
!Ext.cq1('preferences') ? Ext.create('Rambox.view.preferences.Preferences').show() : '';
});
ipc.on('grantPermissions', async function() {
await require('electron').remote.systemPreferences.askForMediaAccess('microphone');
await require('electron').remote.systemPreferences.askForMediaAccess('camera');
});
ipc.on('autoUpdater:check-update', function() {
Rambox.app.checkUpdate();
});
@ -51,8 +40,7 @@ ipc.on('autoUpdater:update-available', function() {
,buttons: Ext.Msg.OK
});
});
ipc.on('autoUpdater:update-downloaded', function(e, info) {
console.log('Update downloaded!', info);
ipc.on('autoUpdater:update-downloaded', function(e, releaseNotes, releaseName, releaseDate, updateURL) {
Ext.cq1('app-main').addDocked({
xtype: 'toolbar'
,dock: 'top'
@ -61,18 +49,18 @@ ipc.on('autoUpdater:update-downloaded', function(e, info) {
'->'
,{
xtype: 'label'
,html: '<b>New version is ready to be installed ('+info.version+')!</b> Click the following button to install it now.'
,html: '<b>New version ready to install ('+releaseName+')!</b> It will be installed the next time Rambox is relaunched.'
}
,{
xtype: 'button'
,text: 'Install now'
,text: 'Relaunch Now'
,handler: function(btn) { ipc.send('autoUpdater:quit-and-install'); }
}
,{
xtype: 'button'
,text: 'Changelog'
,ui: 'decline'
,href: 'https://github.com/ramboxapp/download/releases/latest'
,href: 'https://github.com/saenzramiro/rambox/releases/tag/'+releaseName
}
,'->'
,{
@ -139,40 +127,7 @@ ipc.on('reloadCurrentService', function(e) {
var tab = Ext.cq1('app-main').getActiveTab();
if ( tab.id !== 'ramboxTab' ) tab.reloadService();
});
// Toggle Status Bar
ipc.on('toggleStatusBar', function() {
var tab = Ext.cq1('app-main').getActiveTab();
if ( tab.id !== 'ramboxTab' ) {
tab.down('statusbar').closed ? tab.setStatusBar(tab.record.get('statusbar')) : tab.closeStatusBar();
}
});
// Focus the current service when Alt + Tab or click in webviews textfields
window.addEventListener('focus', function() {
if(Ext.cq1("app-main")) Ext.cq1("app-main").getActiveTab().down('component').el.dom.focus();
});
// Handles zoom from menu
ipc.on('resetzoom-webview', function() {
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
tabPanel.getActiveTab().resetZoom();
});
ipc.on('zoomin-webview', function() {
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
tabPanel.getActiveTab().zoomIn();
});
ipc.on('zoomout-webview', function() {
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
tabPanel.getActiveTab().zoomOut();
});
ipc.on('shortcut:tab', function(e, arg) {
var tabPanel = Ext.cq1('app-main');
if ( arg >= tabPanel.items.indexOf(Ext.getCmp('tbfill')) ) arg++;
tabPanel.setActiveTab(arg);
});

18
app.json

@ -103,12 +103,23 @@
{
"path": "${framework.dir}/build/ext-all-rtl.js"
},
{
"path": "resources/js/auth0-7.1.0.min.js",
"remote": true
},
{
"path": "resources/js/lock-10.2.2/lock.min.js",
"remote": true
},
{
"path": "resources/js/GALocalStorage.js"
},
{
"path": "resources/js/loadscreen.js"
},
{
"path": "env.js"
},
{
"path": "app.js",
"bundle": true
@ -251,7 +262,7 @@
*
*/
"output": {
"base": "../rambox-build",
"base": "${workspace.build.dir}/${build.environment}/${app.name}",
"appCache": {
"enable": true
}
@ -287,10 +298,7 @@
*/
"resources": [
"electron",
"package.json",
"package-lock.json",
"masterpassword.html",
"screenselector.html"
"masterpassword.html"
],
/**

474
app/Application.js

@ -4,7 +4,8 @@ Ext.define('Rambox.Application', {
,name: 'Rambox'
,requires: [
'Rambox.util.MD5'
'Rambox.ux.Auth0'
,'Rambox.util.MD5'
,'Ext.window.Toast'
,'Ext.util.Cookies'
]
@ -22,211 +23,260 @@ Ext.define('Rambox.Application', {
,config: {
totalServicesLoaded: 0
,totalNotifications: 0
,googleURLs: []
}
,launch: function () {
// Set Google Analytics events
ga_storage._setAccount('UA-80680424-1');
ga_storage._trackPageview('/index.html', 'main');
ga_storage._trackEvent('Versions', require('electron').remote.app.getVersion());
const isOnline = require('is-online');
const Mousetrap = require('mousetrap');
(async () => {
await isOnline().then(res => {
var hideNoConnection = ipc.sendSync('getConfig').hideNoConnectionDialog
if ( !res && !hideNoConnection ) {
Ext.get('spinner') ? Ext.get('spinner').destroy() : null;
Ext.get('background') ? Ext.get('background').destroy() : null;
Ext.Msg.show({
title: 'No Internet Connection'
,msg: 'Please, check your internet connection. If you use a Proxy, please go to Preferences to configure it. Rambox will try to re-connect in 10 seconds'
,width: 300
,closable: false
,buttons: Ext.Msg.YESNO
,buttonText: {
yes: 'Ok'
,no: 'Never show this again'
}
,multiline: false
,fn: function(buttonValue, inputText, showConfig) {
if ( buttonValue === 'no' ) {
ipc.send('sConfig', { hideNoConnectionDialog: true });
hideNoConnection = true;
}
}
,icon: Ext.Msg.QUESTION
});
setTimeout(function() {
if ( !hideNoConnection ) ipc.send('reloadApp')
}, 10000)
// Load language for Ext JS library
Ext.Loader.loadScript({url: Ext.util.Format.format("ext/packages/ext-locale/build/ext-locale-{0}.js", localStorage.getItem('locale-auth0') || 'en')});
// Initialize Auth0
Rambox.ux.Auth0.init();
// Set cookies to help Tooltip.io messages segmentation
Ext.util.Cookies.set('version', require('electron').remote.app.getVersion());
if ( Ext.util.Cookies.get('auth0') === null ) Ext.util.Cookies.set('auth0', false);
// Check for updates
if ( require('electron').remote.process.argv.indexOf('--without-update') === -1 && process.platform !== 'win32' ) Rambox.app.checkUpdate(true);
// Add shortcuts to switch services using CTRL + Number
var map = new Ext.util.KeyMap({
target: document
,binding: [
{
key: "\t"
,ctrl: true
,alt: false
,shift: false
,handler: function(key) {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex + 1;
// "cycle" (go to the start) when the end is reached or the end is the spacer "tbfill"
if (i === tabPanel.items.items.length || i === tabPanel.items.items.length - 1 && tabPanel.items.items[i].id === 'tbfill') i = 0;
// skip spacer
while (tabPanel.items.items[i].id === 'tbfill') i++;
tabPanel.setActiveTab(i);
}
}
,{
key: "\t"
,ctrl: true
,alt: false
,shift: true
,handler: function(key) {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex - 1;
if ( i < 0 ) i = tabPanel.items.items.length - 1;
while ( tabPanel.items.items[i].id === 'tbfill' || i < 0 ) i--;
tabPanel.setActiveTab( i );
}
}
,{
key: Ext.event.Event.PAGE_DOWN
,ctrl: true
,alt: false
,shift: false
,handler: function(key) {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex + 1;
// "cycle" (go to the start) when the end is reached or the end is the spacer "tbfill"
if (i === tabPanel.items.items.length || i === tabPanel.items.items.length - 1 && tabPanel.items.items[i].id === 'tbfill') i = 0;
// skip spacer
while (tabPanel.items.items[i].id === 'tbfill') i++;
tabPanel.setActiveTab(i);
}
}
,{
key: Ext.event.Event.PAGE_UP
,ctrl: true
,alt: false
,shift: false
,handler: function(key) {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex - 1;
if ( i < 0 ) i = tabPanel.items.items.length - 1;
while ( tabPanel.items.items[i].id === 'tbfill' || i < 0 ) i--;
tabPanel.setActiveTab( i );
}
}
,{
key: [Ext.event.Event.NUM_PLUS, Ext.event.Event.NUM_MINUS, 187, 189]
,ctrl: true
,alt: false
,shift: false
,handler: function(key) {
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
key === Ext.event.Event.NUM_PLUS || key === 187 ? tabPanel.getActiveTab().zoomIn() : tabPanel.getActiveTab().zoomOut();
}
}
})
})();
if ( !localStorage.getItem('hideMacPermissions') && process.platform === 'darwin' && (require('electron').remote.systemPreferences.getMediaAccessStatus('microphone') !== 'granted' || require('electron').remote.systemPreferences.getMediaAccessStatus('camera') !== 'granted') ) {
console.info('Checking mac permissions...');
Ext.cq1('app-main').addDocked({
xtype: 'toolbar'
,dock: 'top'
,style: {background: '#30BBF3'}
,{
key: [Ext.event.Event.NUM_ZERO, '0']
,ctrl: true
,alt: false
,shift: false
,handler: function(key) {
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
tabPanel.getActiveTab().resetZoom();
}
}
,{
key: "123456789"
,ctrl: true
,alt: false
,handler: function(key) {
key = key - 48;
if ( key >= Ext.cq1('app-main').items.indexOf(Ext.getCmp('tbfill')) ) key++;
Ext.cq1('app-main').setActiveTab(key);
}
}
,{
key: 188 // comma
,ctrl: true
,alt: false
,handler: function(key) {
Ext.cq1('app-main').setActiveTab(0);
}
}
,{
key: Ext.event.Event.F1
,ctrl: false
,alt: false
,shift: false
,handler: function(key) {
var btn = Ext.getCmp('disturbBtn');
btn.toggle();
Ext.cq1('app-main').getController().dontDisturb(btn, true);
}
}
,{
key: Ext.event.Event.F2
,ctrl: false
,alt: false
,shift: false
,handler: function(key) {
var btn = Ext.getCmp('lockRamboxBtn');
Ext.cq1('app-main').getController().lockRambox(btn);
}
}
]
});
// Mouse Wheel zooming
document.addEventListener('mousewheel', function(e) {
if( e.ctrlKey ) {
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
if ( delta === 1 ) { // Zoom In
tabPanel.getActiveTab().zoomIn();
} else { // Zoom Out
tabPanel.getActiveTab().zoomOut();
}
}
});
// Define default value
if ( localStorage.getItem('dontDisturb') === null ) localStorage.setItem('dontDisturb', false);
ipc.send('setDontDisturb', localStorage.getItem('dontDisturb')); // We store it in config
if ( localStorage.getItem('locked') ) {
console.info('Lock Rambox:', 'Enabled');
Ext.cq1('app-main').getController().showLockWindow();
}
// Synchronization problem in version 0.5.3 steps to fix it
if ( localStorage.getItem('id_token') && localStorage.getItem('refresh_token') === null ) {
var win = Ext.create('Ext.window.Window', {
title: 'Backup your services'
,autoShow: true
,modal: true
,closable: false
,resizable: false
,bodyPadding: '0 15 15 15'
,width: 500
,layout: 'card'
,items: [
'->'
{
xtype: 'container'
,html: '<h1>Synchronization problem fixed!</h1>In previous version, we had a bug that backing up your services throw an error. Now is fixed, but you will need to follow two simple steps to make it work.<br><br>If you decide not to do it now, you can cancel but it will ask you again next time you open Rambox until you do it.'
}
,{
xtype: 'label'
,html: '<b>Rambox CE needs permissions to use Microphone and Camera for the apps.</b>'
xtype: 'container'
,html: '<h1>Login again</h1>Just click the "Sign in" button at the bottom-right of this window to sign in again with the same account you used before.'
}
,{
xtype: 'button'
,text: 'Grant permissions'
xtype: 'container'
,html: '<h1>Backup</h1>To finish, click the "Sync!" button to backup your current services and that\'s all!'
}
]
,buttons: [
{
text: locale['button[1]']
,ui: 'decline'
,handler: async function(btn) {
await require('electron').remote.systemPreferences.askForMediaAccess('microphone');
await require('electron').remote.systemPreferences.askForMediaAccess('camera');
Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true);
,handler: function() {
win.close();
}
}
,'->'
,{
xtype: 'button'
,text: 'Never ask again'
,ui: 'decline'
text: 'Start'
,handler: function(btn) {
Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true);
localStorage.setItem('hideMacPermissions', true);
btn.hide();
btn.nextSibling('#signin').show();
win.getLayout().setActiveItem(1);
}
}
,'->'
,{
glyph: 'xf00d@FontAwesome'
,baseCls: ''
,style: 'cursor:pointer;'
,handler: function(btn) { Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); }
}
]
});
}
Ext.getStore('ServicesList').load(function (records, operations, success) {
if (!success) {
Ext.cq1('app-main').addDocked({
xtype: 'toolbar'
,dock: 'top'
,ui: 'servicesnotloaded'
,style: { background: '#efef6d' }
,items: [
'->'
,{
xtype: 'label'
,html: '<b>Services couldn\'t be loaded, some Rambox features will not be available.</b>'
}
,{
xtype: 'button'
,text: 'Reload'
,handler: function() { ipc.send('reloadApp'); }
text: 'Sign in'
,itemId: 'signin'
,hidden: true
,handler: function(btn) {
Rambox.ux.Auth0.backupCurrent = true;
Rambox.ux.Auth0.login();
Ext.defer(Rambox.ux.Auth0.logout, 1000);
btn.hide();
btn.nextSibling('#sync').show();
win.getLayout().setActiveItem(2);
}
,'->'
,{
glyph: 'xf00d@FontAwesome'
,baseCls: ''
,style: 'cursor:pointer;'
,handler: function(btn) { Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); }
}
,{
text: 'Sync!'
,itemId: 'sync'
,hidden: true
,handler: function() {
Rambox.ux.Auth0.backupConfiguration(function() {
win.close();
Rambox.ux.Auth0.backupCurrent = false;
});
}
]
});
}
// Load language for Ext JS library
Ext.Loader.loadScript({url: Ext.util.Format.format("ext/packages/ext-locale/build/ext-locale-{0}.js", localStorage.getItem('locale-extjs') || 'en')});
// Set Google URLs
Rambox.app.config.googleURLs = [
"accounts.google.com/ServiceLogin",
"accounts.google.com/signin",
"accounts.google.com/_/lookup/accountlookup",
"accounts.google.com/o/oauth2",
"accounts.google.com/_/signin",
"accounts.google.com/AddSession?",
"accounts.google.com/_/"
];
// Shortcuts
const platform = require('electron').remote.process.platform;
// Prevents default behaviour of Mousetrap, that prevents shortcuts in textareas
Mousetrap.prototype.stopCallback = function(e, element, combo) {
return false;
};
// Add shortcuts to switch services using CTRL + Number
Mousetrap.bind(platform === 'darwin' ? ["command+1","command+2","command+3","command+4","command+5","command+6","command+7","command+8","command+9"] : ["ctrl+1","ctrl+2","ctrl+3","ctrl+4","ctrl+5","ctrl+6","ctrl+7","ctrl+8","ctrl+9"], function(e, combo) { // GROUPS
var tabPanel = Ext.cq1('app-main');
var arg = parseInt(e.key);
if ( arg >= tabPanel.items.indexOf(Ext.getCmp('tbfill')) ) arg++;
tabPanel.setActiveTab(arg);
});
// Add shortcut to main tab (ctrl+,)
Mousetrap.bind(platform === 'darwin' ? 'command+,' : 'ctrl+,', (e, combo) => {
Ext.cq1('app-main').setActiveTab(0);
});
// Add shortcuts to navigate through services
Mousetrap.bind(['ctrl+tab', 'ctrl+pagedown'], (e, combo) => {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex + 1;
// "cycle" (go to the start) when the end is reached or the end is the spacer "tbfill"
if (i === tabPanel.items.items.length || i === tabPanel.items.items.length - 1 && tabPanel.items.items[i].id === 'tbfill') i = 0;
// skip spacer
while (tabPanel.items.items[i].id === 'tbfill') i++;
tabPanel.setActiveTab(i);
});
Mousetrap.bind(['ctrl+shift+tab', 'ctrl+pageup'], (e, combo) => {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex - 1;
if ( i < 0 ) i = tabPanel.items.items.length - 1;
while ( tabPanel.items.items[i].id === 'tbfill' || i < 0 ) i--;
tabPanel.setActiveTab(i);
});
// Add shortcut to search inside a service
Mousetrap.bind(process.platform === 'darwin' ? ['command+alt+f'] : ['shift+alt+f'], (e, combo) => {
var currentTab = Ext.cq1('app-main').getActiveTab();
if ( currentTab.getWebView ) currentTab.showSearchBox(true);
});
// Add shortcut to Do Not Disturb
Mousetrap.bind(platform === 'darwin' ? ["command+alt+d"] : ["shift+alt+d"], function(e, combo) {
var btn = Ext.getCmp('disturbBtn');
btn.toggle();
Ext.cq1('app-main').getController().dontDisturb(btn, true);
});
// Add shortcut to Lock Rambox
Mousetrap.bind(platform === 'darwin' ? ['command+alt+l'] : ['shift+alt+l'], (e, combo) => {
var btn = Ext.getCmp('lockRamboxBtn');
Ext.cq1('app-main').getController().lockRambox(btn);
});
// Mouse Wheel zooming
document.addEventListener('mousewheel', function(e) {
if( e.ctrlKey ) {
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
var tabPanel = Ext.cq1('app-main');
if ( tabPanel.items.indexOf(tabPanel.getActiveTab()) === 0 ) return false;
if ( delta === 1 ) { // Zoom In
tabPanel.getActiveTab().zoomIn();
} else { // Zoom Out
tabPanel.getActiveTab().zoomOut();
}
}
]
});
}
// Define default value
if ( localStorage.getItem('dontDisturb') === null ) localStorage.setItem('dontDisturb', false);
ipc.send('setDontDisturb', localStorage.getItem('dontDisturb')); // We store it in config
if ( localStorage.getItem('locked') ) {
console.info('Lock Rambox:', 'Enabled');
Ext.cq1('app-main').getController().showLockWindow();
}
Ext.getStore('Services').load();
});
// Remove spinner
Ext.get('spinner').destroy();
}
,updateTotalNotifications: function( newValue, oldValue ) {
@ -247,6 +297,60 @@ Ext.define('Rambox.Application', {
}
,checkUpdate: function(silence) {
ipc.send('autoUpdater:check-for-updates');
console.info('Checking for updates...');
Ext.Ajax.request({
url: 'http://rambox.pro/api/latestversion.json'
,method: 'GET'
,success: function(response) {
var json = Ext.decode(response.responseText);
var appVersion = new Ext.Version(require('electron').remote.app.getVersion());
if ( appVersion.isLessThan(json.version) ) {
console.info('New version is available', json.version);
Ext.cq1('app-main').addDocked({
xtype: 'toolbar'
,dock: 'top'
,ui: 'newversion'
,items: [
'->'
,{
xtype: 'label'
,html: '<b>'+locale['app.update[0]']+'</b> ('+json.version+')' + ( process.platform === 'win32' ? ' Is downloading in the background and you will notify when is ready to install it.' : '' )
}
,{
xtype: 'button'
,text: locale['app.update[1]']
,href: process.platform === 'darwin' ? 'https://getrambox.herokuapp.com/download/'+process.platform+'_'+process.arch : 'https://github.com/saenzramiro/rambox/releases/latest'
,hidden: process.platform === 'win32'
}
,{
xtype: 'button'
,text: locale['app.update[2]']
,ui: 'decline'
,tooltip: 'Click here to see more information about the new version.'
,href: 'https://github.com/saenzramiro/rambox/releases/tag/'+json.version
}
,'->'
,{
glyph: 'xf00d@FontAwesome'
,baseCls: ''
,style: 'cursor:pointer;'
,handler: function(btn) { Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); }
}
]
});
if ( process.platform === 'win32' ) ipc.send('autoUpdater:check-for-updates');
return;
} else if ( !silence ) {
Ext.Msg.show({
title: locale['app.update[3]']
,message: locale['app.update[4]']
,icon: Ext.Msg.INFO
,buttons: Ext.Msg.OK
});
}
console.info('Your version is the latest. No need to update.');
}
});
}
});

29
app/README.md

@ -1,29 +0,0 @@
# ./controller
This folder contains the application's global controllers.
ViewControllers are located alongside their respective view class in `./view`.
These controllers are used for routing and other activities that span all views.
# ./model
This folder contains the application's (data) Model classes.
# ./view
This folder contains the views as well as ViewModels and ViewControllers depending on the application's architecture.
Pure MVC applications may not have ViewModels, for example.
For MVCVM applications or MVC applications that use ViewControllers, the following directory structure is recommended:
```text
./view/
foo/ # Some meaningful grouping of one or more views
Foo.js # The view class
FooController.js # The controller for Foo (a ViewController)
FooModel.js # The ViewModel for Foo
```
This structure helps keep these closely related classes together and easily identifiable in most tabbed IDE's or text editors.
# ./store
This folder contains any number of store instances or types that can then be reused in the application.

30
app/Readme.md

@ -0,0 +1,30 @@
# ./controller
This folder contains the application's global controllers. ViewControllers are located
alongside their respective view class in `"./view"`. These controllers are used for routing
and other activities that span all views.
# ./model
This folder contains the application's (data) Model classes.
# ./view
This folder contains the views as well as ViewModels and ViewControllers depending on the
application's architecture. Pure MVC applications may not have ViewModels, for example. For
MVCVM applications or MVC applications that use ViewControllers, the following directory
structure is recommended:
./view/
foo/ # Some meaningful grouping of one or more views
Foo.js # The view class
FooController.js # The controller for Foo (a ViewController)
FooModel.js # The ViewModel for Foo
This structure helps keep these closely related classes together and easily identifiable in
most tabbed IDE's or text editors.
# ./store
This folder contains any number of store instances or types that can then be reused in the
application.

5
app/model/README.md → app/model/Readme.md

@ -15,10 +15,13 @@ Structure of a service entry:
|note|Additional info to display when adding the service.|no|
|manual_notifications|Set to `true` to let Rambox trigger notifications. Can be used for services that doesn't support browser notifications.|no|
|js_unread|JavaScript code for setting the unread count (see below).|no|
|dont_update_unread_from_title|Set to `true` to prevent updating the unread count from the window title (see below).|no|
### Setting the unread count
While by default the unread count is determined by looking for ` (COUNT)` to the window title, this describes the preferred way of doing it:
While there is also a way to set the unread count by adding ` (COUNT)` to the window title, this describes the preferred way of doing it:
First set `dont_update_unread_from_title` in the service config to `true`.
Code provided by `js_unread` will be injected into the service website.
You can retrieve the unread count in this JavaScript code e.g. by parsing elements.

2
app/model/Service.js

@ -56,7 +56,7 @@ Ext.define('Rambox.model.Service', {
},{
name: 'trust'
,type: 'boolean'
,defaultValue: false
,defaultValue: true
},{
name: 'enabled'
,type: 'boolean'

4
app/model/ServiceList.js

@ -48,5 +48,9 @@ Ext.define('Rambox.model.ServiceList', {
name: 'custom_domain'
,type: 'boolean'
,defaultValue: false
},{
name: 'dont_update_unread_from_title'
,type: 'boolean'
,defaultValue: false
}]
});

40
app/package.json

@ -0,0 +1,40 @@
{
"name": "Rambox",
"productName": "Rambox",
"version": "0.5.11",
"description": "Rambox",
"main": "electron/main.js",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/saenzramiro/rambox.git"
},
"bugs": {
"url": "https://github.com/saenzramiro/rambox/issues"
},
"homepage": "http://rambox.pro",
"keywords": [
"Rambox",
"messaging",
"app",
"slack",
"whatsapp",
"facebook",
"messenger",
"telegram",
"google",
"hangouts",
"skype"
],
"author": "Ramiro Saenz <saenzramiro@gmail.com>",
"license": "GPL-3.0",
"dependencies": {
"@exponent/electron-cookies": "2.0.0",
"auto-launch-patched": "5.0.2",
"electron-config": "0.2.1",
"electron-is-dev": "^0.1.2",
"mime": "^1.4.0",
"rimraf": "2.6.1",
"tmp": "0.0.28"
}
}

0
app/store/README.md → app/store/Readme.md

42
app/store/Services.js

@ -8,9 +8,8 @@ Ext.define('Rambox.store.Services', {
,model: 'Rambox.model.Service'
,autoLoad: false
,autoLoad: true
,autoSync: true
,pageSize: 0
,groupField: 'align'
,sorters: [
@ -21,21 +20,28 @@ Ext.define('Rambox.store.Services', {
]
,listeners: {
load: function( store, records, successful ) {
load: function( store, records, successful ) {
Ext.cq1('app-main').suspendEvent('add');
var servicesLeft = [];
var servicesRight = [];
store.each(function(service) {
// Fix some services with bad IDs
// TODO: Remove in next release
switch ( service.get('type') ) {
case 'office365':
service.set('type', 'outlook365');
break;
case ' irccloud':
service.set('type', 'irccloud');
break;
default:
break;
}
// If the service is disabled, we dont add it to tab bar
if ( !service.get('enabled') ) return;
// Rebranded app
if ( service.get('type') === 'spark' ) {
service.set('type', 'webexteams');
service.set('logo', 'webexteams.png');
}
var cfg = {
xtype: 'webview'
,id: 'tab_'+service.get('id')
@ -48,7 +54,6 @@ Ext.define('Rambox.store.Services', {
,displayTabUnreadCounter: service.get('displayTabUnreadCounter')
,enabled: service.get('enabled')
,record: service
,useragent: ipc.sendSync('getConfig').user_agent
,tabConfig: {
service: service
}
@ -60,25 +65,8 @@ Ext.define('Rambox.store.Services', {
if ( !Ext.isEmpty(servicesLeft) ) Ext.cq1('app-main').insert(1, servicesLeft);
if ( !Ext.isEmpty(servicesRight) ) Ext.cq1('app-main').add(servicesRight);
// Set default active service
const config = ipc.sendSync('getConfig');
switch ( config.default_service ) {
case 'last':
Ext.cq1('app-main').setActiveTab(localStorage.getItem('last_active_service'));
break;
case 'ramboxTab':
break;
default:
if ( Ext.getCmp('tab_'+config.default_service) ) Ext.cq1('app-main').setActiveTab('tab_'+config.default_service);
break;
}
store.suspendEvent('load');
Ext.cq1('app-main').resumeEvent('add');
}
,datachanged: function(store, eOpts) {
var isEmpty = store.getCount() > 0 ? false : true;
Ext.cq1('app-main').getViewModel().set('emptyServices', isEmpty);
}
}
});

834
app/store/ServicesList.js

@ -9,28 +9,9 @@ Ext.define('Rambox.store.ServicesList', {
,model: 'Rambox.model.ServiceList'
,proxy: {
type: 'ajax',
url: 'resources/services.json',
reader: {
type: 'json',
rootProperty: 'responseText'
}
}
,listeners: {
load: function () {
Ext.get('spinner') ? Ext.get('spinner').destroy() : null;
Ext.get('background') ? Ext.get('background').destroy() : null;
this.add({
id: 'custom'
,logo: 'custom.png'
,name: '_Custom Service'
,description: locale['services[38]']
,url: '___'
,type: 'custom'
,allow_popups: true
})
}
type: 'memory'
}
,sorters: [{
property: 'name'
,direction: 'ASC'
@ -39,4 +20,815 @@ Ext.define('Rambox.store.ServicesList', {
,autoLoad: true
,autoSync: true
,pageSize: 100000
,data: [
{
id: 'whatsapp'
,logo: 'whatsapp.png'
,name: 'WhatsApp'
,description: locale['services[0]']
,url: 'https://web.whatsapp.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("infinite-list-item"),b=0;for(i=0;i<a.length;i++)if(!(a[i].getElementsByClassName("icon-muted").length>0||0===a[i].getElementsByClassName("unread-count").length)){var c=parseInt(a[i].getElementsByClassName("unread-count")[0].innerHTML.trim());b+=isNaN(c)?0:c}updateBadge(b)}function updateBadge(count) { if (count && count >= 1) { rambox.setUnreadCount(count); } else { rambox.clearUnreadCount(); } }setInterval(checkUnread,1e3);'
,dont_update_unread_from_title: true
},
{
id: 'slack'
,logo: 'slack.png'
,name: 'Slack'
,description: locale['services[1]']
,url: 'https://___.slack.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=$(".p-channel_sidebar__channel--unread").length,a=0;$(".p-channel_sidebar__badge").each(function(){a+=isNaN(parseInt($(this).html()))?0:parseInt($(this).html())}),updateBadge(e,a)}function updateBadge(e,a){var n=a>0?"("+a+") ":e>0?"(•) ":"";document.title=n+originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);'
},
{
id: 'noysi'
,logo: 'noysi.png'
,name: 'Noysi'
,description: locale['services[2]']
,url: 'https://noysi.com/#/identity/sign-in'
,type: 'messaging'
},
{
id: 'messenger'
,logo: 'messenger.png'
,name: 'Messenger'
,description: locale['services[3]']
,url: 'https://www.messenger.com/login/'
,type: 'messaging'
,titleBlink: true
,note: 'To enable desktop notifications, you have to go to Options inside Messenger.'
},
{
id: 'skype'
,logo: 'skype.png'
,name: 'Skype'
,description: locale['services[4]']
,url: 'https://web.skype.com/'
,type: 'messaging'
,userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586'
,note: 'Text and Audio calls are supported only. <a href="https://github.com/saenzramiro/rambox/wiki/Skype" target="_blank">Read more...</a>'
},
{
id: 'hangouts'
,logo: 'hangouts.png'
,name: 'Hangouts'
,description: locale['services[5]']
,url: 'https://hangouts.google.com/'
,type: 'messaging'
,titleBlink: true
,manual_notifications: true
,dont_update_unread_from_title: true
,js_unread: 'function checkUnread(){updateBadge(document.getElementById("hangout-landing-chat").lastChild.contentWindow.document.body.getElementsByClassName("ee").length)}function updateBadge(e){e>=1?rambox.setUnreadCount(e):rambox.clearUnreadCount()}setInterval(checkUnread,3000);'
},
{
id: 'hipchat'
,logo: 'hipchat.png'
,name: 'HipChat'
,description: locale['services[6]']
,url: 'https://___.hipchat.com/chat'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("hc-badge"),t=0;for(i=0;i<e.length;i++)t+=parseInt(e[i].innerHTML.trim());updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
,custom_domain: true
},
{
id: 'telegram'
,logo: 'telegram.png'
,name: 'Telegram'
,description: locale['services[7]']
,url: 'https://web.telegram.org/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("im_dialog_badge badge"),t=0;for(i=0;i<e.length;i++)if(!e[i].classList.contains("im_dialog_badge_muted")){t+=parseInt(e[i].innerHTML.trim())}updateBadge(t)}function updateBadge(e){e>=1?rambox.setUnreadCount(e):rambox.clearUnreadCount()}setInterval(checkUnread,3000);'
,dont_update_unread_from_title: true
},
{
id: 'wechat'
,logo: 'wechat.png'
,name: 'WeChat'
,description: locale['services[8]']
,url: 'https://web.wechat.com/'
,type: 'messaging'
},
{
id: 'gmail'
,logo: 'gmail.png'
,name: 'Gmail'
,description: locale['services[9]']
,url: 'https://mail.google.com/mail/'
,type: 'email'
,allow_popups: true
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("aim")[0];updateBadge(-1!=a.textContent.indexOf("(")&&(t=parseInt(a.textContent.replace(/[^0-9]/g,""))))}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,note: 'To enable desktop notifications, you have to go to Settings inside Gmail. <a href="https://support.google.com/mail/answer/1075549?ref_topic=3394466" target="_blank">Read more...</a>'
,dont_update_unread_from_title: true
},
{
id: 'inbox'
,logo: 'inbox.png'
,name: 'Inbox'
,description: locale['services[10]']
,url: 'http://inbox.google.com/?cid=imp'
,type: 'email'
,manual_notifications: true
,js_unread: 'function checkUnread(){updateBadge(document.getElementsByClassName("ss").length)}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);'
,note: 'Please be sure to sign out of Hangouts inside Inbox, as it causes problems. <a href="https://github.com/saenzramiro/rambox/wiki/Inbox" target="_blank">Read more...</a>'
},
{
id: 'chatwork'
,logo: 'chatwork.png'
,name: 'ChatWork'
,description: locale['services[11]']
,url: 'https://www.chatwork.com/login.php'
,type: 'messaging'
,note: 'To enable desktop notifications, you have to go to Options inside ChatWork.'
},
{
id: 'groupme'
,logo: 'groupme.png'
,name: 'GroupMe'
,description: locale['services[12]']
,url: 'https://web.groupme.com/signin'
,type: 'messaging'
,note: 'To enable desktop notifications, you have to go to Options inside GroupMe. To count unread messages, be sure to be in Chats.'
,js_unread: 'function checkUnread(){var a=document.querySelectorAll(".badge-count"),b=0;for(i=0;i<a.length;i++)b+=parseInt(a[i].innerHTML.trim());updateBadge(b)}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,dont_update_unread_from_title: true
},
{
id: 'grape'
,logo: 'grape.png'
,name: 'Grape'
,description: locale['services[13]']
,url: 'https://chatgrape.com/accounts/login/'
,type: 'messaging'
},
{
id: 'gitter'
,logo: 'gitter.png'
,name: 'Gitter'
,description: locale['services[14]']
,url: 'https://gitter.im/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("room-item__unread-indicator"),t=0;for(i=0;i<e.length;i++)t+=isNaN(parseInt(e[i].innerHTML.trim())) ? 0 : parseInt(e[i].innerHTML.trim());updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'steam'
,logo: 'steam.png'
,name: 'Steam Chat'
,description: locale['services[15]']
,url: 'https://steamcommunity.com/chat'
,type: 'messaging'
,note: 'To enable desktop notifications, you have to go to Options inside Steam Chat.'
,js_unread: 'CTitleManager.UpdateTitle = function(){};function checkUnread(){var e=document.getElementsByClassName("unread_message_count_value"),t=0;for(i=0;i<e.length;i++)t+=isNaN(parseInt(e[i].innerHTML.trim())) || e[i].parentNode.style.display === "none" ? 0 : parseInt(e[i].innerHTML.trim());updateBadge(t/2)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'discord'
,logo: 'discord.png'
,name: 'Discord'
,description: locale['services[16]']
,url: 'https://discordapp.com/login'
,type: 'messaging'
,titleBlink: true
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("guild unread").length,b=0,c=document.getElementsByClassName("badge");for(i=0;i<c.length;i++)b+=parseInt(c[i].innerHTML.trim());updateBadge(a,b)}function updateBadge(a,b){var c=b>0?"("+b+") ":a>0?"(•) ":"";document.title=c+originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);'
,note: 'To enable desktop notifications, you have to go to Options inside Discord.'
},
{
id: 'outlook'
,logo: 'outlook.png'
,name: 'Outlook'
,description: locale['services[17]']
,url: 'https://mail.live.com/'
,type: 'email'
,manual_notifications: true
,js_unread: 'function checkUnread(){var a=$(".subfolders [role=treeitem]:first .treeNodeRowElement").siblings().last().text();updateBadge(""===a?0:parseInt(a))}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);'
},
{
id: 'outlook365'
,logo: 'outlook365.png'
,name: 'Outlook 365'
,description: locale['services[18]']
,url: 'https://outlook.office.com/owa/'
,type: 'email'
,manual_notifications: true
,js_unread: 'function checkUnread(){var a=$(".subfolders [role=treeitem]:first .treeNodeRowElement").siblings().last().text();updateBadge(""===a?0:parseInt(a))}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);'
},
{
id: 'yahoo'
,logo: 'yahoo.png'
,name: 'Yahoo! Mail'
,description: locale['services[19]']
,url: 'https://mail.yahoo.com/'
,type: 'email'
,note: 'To enable desktop notifications, you have to go to Options inside Yahoo! Mail.'
,userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
},
{
id: 'protonmail'
,logo: 'protonmail.png'
,name: 'ProtonMail'
,description: locale['services[20]']
,url: 'https://mail.protonmail.com/inbox'
,type: 'email'
},
{
id: 'protonmailch'
,logo: 'protonmail.png'
,name: 'ProtonMail CH'
,description: locale['services[20]']
,url: 'https://app.protonmail.ch/inbox'
,type: 'email'
,note: 'Read <a href="https://protonmail.com/support/knowledge-base/what-is-the-difference-between-protonmail-com-and-protonmail-ch/" target="_blank">HERE</a> to see the differences between protonmail.com and protonmail.ch.'
},
{
id: 'tutanota'
,logo: 'tutanota.png'
,name: 'Tutanota'
,description: locale['services[21]']
,url: 'https://app.tutanota.de/'
,type: 'email'
},
{
id: 'hushmail'
,logo: 'hushmail.png'
,name: 'Hushmail'
,description: locale['services[22]']
,url: 'https://www.hushmail.com/hushmail/index.php'
,type: 'email'
},
{
id: 'missive'
,logo: 'missive.png'
,name: 'Missive'
,description: locale['services[23]']
,url: 'https://mail.missiveapp.com/login'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("unseen-count"),t=0;for(i=0;i<e.length;i++)t+=parseInt(e[i].innerHTML.trim());updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'rocketchat'
,logo: 'rocketchat.png'
,name: 'Rocket Chat'
,description: locale['services[24]']
,url: '___'
,type: 'messaging'
,note: 'You have to use this service by signing in with your email or username (No SSO allowed yet).'
},
{
id: 'wire'
,logo: 'wire.png'
,name: 'Wire'
,description: locale['services[25]']
,url: 'https://app.wire.com/'
,type: 'messaging'
},
{
id: 'sync'
,logo: 'sync.png'
,name: 'Sync'
,description: locale['services[26]']
,url: 'https://m.wantedly.com/login'
,type: 'messaging'
},
{
id: 'bearychat'
,logo: 'bearychat.png'
,name: 'BearyChat'
,url: 'https://___.bearychat.com/'
,type: 'messaging'
},
{
id: 'yahoomessenger'
,logo: 'yahoomessenger.png'
,name: 'Yahoo! Messenger'
,description: locale['services[28]']
,url: 'https://messenger.yahoo.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){updateBadge(document.getElementsByClassName("list-item-unread-indicator").length)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'voxer'
,logo: 'voxer.png'
,name: 'Voxer'
,description: locale['services[29]']
,url: 'https://web.voxer.com/'
,type: 'messaging'
},
{
id: 'dasher'
,logo: 'dasher.png'
,name: 'Dasher'
,description: locale['services[30]']
,url: 'https://dasher.im/'
,type: 'messaging'
},
{
id: 'flowdock'
,logo: 'flowdock.png'
,name: 'Flowdock'
,description: locale['services[31]']
,url: 'https://www.flowdock.com/login'
,type: 'messaging'
},
{
id: 'mattermost'
,logo: 'mattermost.png'
,name: 'Mattermost'
,description: locale['services[32]']
,url: '___'
,type: 'messaging'
,js_unread: 'Object.defineProperty(document,"title",{configurable:!0,set:function(a){document.getElementsByTagName("title")[0].innerHTML=a[0]==="*"?"(•) Mattermost":a},get:function(){return document.getElementsByTagName("title")[0].innerHTML}});'
},
{
id: 'dingtalk'
,logo: 'dingtalk.png'
,name: 'DingTalk'
,description: locale['services[33]']
,url: 'https://im.dingtalk.com/'
,type: 'messaging'
},
{
id: 'mysms'
,logo: 'mysms.png'
,name: 'mysms'
,description: locale['services[34]']
,url: 'https://app.mysms.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("unread"),t=0;for(i=0;i<e.length;i++)t+=parseInt(e[i].firstChild.innerHTML.trim());updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}"https://app.mysms.com/#login"===document.baseURI&&(document.getElementsByClassName("innerPanel")[0].rows[0].style.display="none",document.getElementsByClassName("innerPanel")[0].rows[1].cells[0].firstElementChild.style.display="none",document.getElementsByClassName("msisdnLoginPanel")[0].style.display="inline");var originalTitle=document.title;setInterval(checkUnread,3000);'
,note: 'You have to use this service by signing in with your mobile number.'
},
{
id: 'icq'
,logo: 'icq.png'
,name: 'ICQ'
,description: locale['services[35]']
,url: 'https://web.icq.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){updateBadge(parseInt(document.getElementsByClassName("nwa-msg-counter")[0].style.display==="block"?document.getElementsByClassName("nwa-msg-counter")[0].innerHTML.trim():0))}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'tweetdeck'
,logo: 'tweetdeck.png'
,name: 'TweetDeck'
,description: locale['services[36]']
,url: 'https://tweetdeck.twitter.com/'
,type: 'messaging'
},
{
id: 'custom'
,logo: 'custom.png'
,name: '_Custom Service'
,description: locale['services[38]']
,url: '___'
,type: 'custom'
,allow_popups: true
},
{
id: 'zinc'
,logo: 'zinc.png'
,name: 'Zinc'
,description: locale['services[39]']
,url: 'https://zinc-app.com/'
,type: 'messaging'
},
{
id: 'freenode'
,logo: 'freenode.png'
,name: 'FreeNode'
,description: locale['services[40]']
,url: 'https://webchat.freenode.net/'
,type: 'messaging'
},
{
id: 'mightytext'
,logo: 'mightytext.png'
,name: 'Mighty Text'
,description: locale['services[41]']
,url: 'https://mightytext.net/web/'
,type: 'messaging'
},
{
id: 'roundcube'
,logo: 'roundcube.png'
,name: 'Roundcube'
,description: locale['services[42]']
,url: '___'
,type: 'email'
},
{
id: 'horde'
,logo: 'horde.png'
,name: 'Horde'
,description: locale['services[43]']
,url: '___'
,type: 'email'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("count"),t=0;for(i=0;i<e.length;i++)t+=parseInt(e[i].innerHTML.match(/\d+/g));updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
,note: 'To enable desktop notifications and automatic mail check, you have to go to Options inside Horde.'
},
{
id: 'squirrelmail'
,logo: 'squirrelmail.png'
,name: 'SquirrelMail'
,description: locale['services[44]']
,url: '___'
,type: 'email'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("leftunseen"),t=0;for(i=0;i<e.length;i++)t+=parseInt(e[i].innerHTML);updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'zohoemail'
,logo: 'zohoemail.png'
,name: 'Zoho Email'
,description: locale['services[45]']
,url: 'https://mail.zoho.com/'
,type: 'email'
,js_unread: 'zmail.aInfo[zmail.accId].mailId = "a";'
,note: 'To enable desktop notifications, you have to go to Settings inside Zoho Email.'
},
{
id: 'zohochat'
,logo: 'zohochat.png'
,name: 'Zoho Chat'
,description: locale['services[46]']
,url: 'https://chat.zoho.com/'
,type: 'messaging'
,js_unread: 'NotifyByTitle.show = function(){};NotifyByTitle.start = function(){};NotifyByTitle.stop = function(){};function checkUnread(){var t=0;$(".msgnotify").each(function() { t += isNaN(parseInt($(this).html())) ? 0 : parseInt(parseInt($(this).html())) });updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'aol'
,logo: 'aol.png'
,name: 'Aol'
,description: 'Free and simple (old) webmail service.'
,url: 'https://mail.aol.com/'
,type: 'email'
},
{
id: 'glip'
,logo: 'glip.png'
,name: 'Glip'
,description: 'Glip is fully searchable, real-time group chat & video chat, task management, file sharing, calendars and more.'
,url: 'https://glip.com/'
,type: 'messaging'
,note: 'To enable desktop notifications, you have to go to Options inside Glip.'
},
{
id: 'yandex'
,logo: 'yandex.png'
,name: 'Yandex Mail'
,description: 'Yandex is a free webmail service with unlimited mail storage, protection from viruses and spam, access from web interface, etc.'
,url: 'https://mail.yandex.com/'
,type: 'email'
,js_unread: 'function checkUnread(){var t=parseInt($(".mail-MessagesFilters-Item_unread .mail-LabelList-Item_count").html());updateBadge(isNaN(t)?0:t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'irccloud'
,logo: 'irccloud.png'
,name: 'IRCCloud'
,description: 'IRCCloud is a modern IRC client that keeps you connected, with none of the baggage.'
,url: 'https://www.irccloud.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var t=0;[].map.call(document.querySelectorAll(".bufferBadges > .badge"),n=>n.textContent?parseInt(n.textContent,10):0).reduce((x,y)=>x+y,0);updateBadge(t)}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
,custom_domain: true
},
{
id: 'ryver'
,logo: 'ryver.png'
,name: 'Ryver'
,description: 'Ryver is a team communication tool that organizes team collaboration, chats, files, and even emails into a single location, for any size team, for FREE.'
,url: 'https://___.ryver.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){updateBadge(parseInt(document.getElementsByClassName("scene-space-tab-button--flash").length))}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
},
{
id: 'aim'
,logo: 'aim.png'
,name: 'Aim'
,description: 'Aim offers free Text Messaging, Group Conversations, Media Sharing, Social Notifications, and more.'
,url: 'http://aim.com/'
,type: 'messaging'
},
{
id: 'kiwi'
,logo: 'kiwi.png'
,name: 'Kiwi IRC'
,description: 'KiwiIRC makes Web IRC easy. A hand-crafted IRC client that you can enjoy. Designed to be used easily and freely.'
,url: 'https://kiwiirc.com/client'
,type: 'messaging'
,js_unread: 'function getUnreadCount(){var a=0;$(".activity").each(function(){a+=parseInt($(this).html())});var b=!1;return $(".panel[style*=\'display:block\'] .msg").each(function(){b?a++:$(this).hasClass("last_seen")&&(b=!0)}),a}function updateTitle(a){count=getUnreadCount(),cleanTitle=a.match(re),null!==cleanTitle&&cleanTitle.length>1?cleanTitle=cleanTitle[1]:cleanTitle=a,a=count>0?"("+getUnreadCount()+") "+cleanTitle:cleanTitle,$("title").text(a)}var re=/\(\d+\)[ ](.*)/;Object.defineProperty(document,"title",{configurable:!0,set:function(a){updateTitle(a)},get:function(){return $("title").text()}}),setInterval(function(){updateTitle(document.title)},3e3);'
,custom_domain: true
},
{
id: 'icloud'
,logo: 'icloud.png'
,name: 'iCloud Mail'
,description: 'iCloud makes sure you always have the latest versions of your most important things — documents, photos, notes, contacts, and more — on all your devices. It can even help you locate a missing iPhone, iPad, iPod touch or Mac.'
,url: 'https://www.icloud.com/#mail'
,type: 'email'
,js_unread: 'function checkUnread(){updateBadge(document.querySelector(".current-app").querySelector(".sb-badge").style.display==="none"?0:parseInt(document.querySelector(".current-app").querySelector(".text").innerHTML.trim()))}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,dont_update_unread_from_title: true
},
{
id: 'rainloop'
,logo: 'rainloop.png'
,name: 'RainLoop'
,description: 'RainLoop Webmail - Simple, modern & fast web-based email client.'
,url: '___'
,type: 'email'
,js_unread: 'function checkUnread(){var a=document.querySelectorAll(".e-item .e-link:not(.hidden) .badge.pull-right.count"),b=0;for(i=0;i<a.length;i++)parseInt(a[i].textContent.trim())%1===0&&(b+=parseInt(a[i].textContent.trim()));updateBadge(b)}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,1e3);'
},
{
id: 'amium'
,logo: 'amium.png'
,name: 'Amium'
,description: 'Amium turns any file into a real-time activity feed and conversation. So you can work better, together.'
,url: 'https://___.amium.com/'
,type: 'messaging'
},
{
id: 'hootsuite'
,logo: 'hootsuite.png'
,name: 'Hootsuite'
,description: 'Enhance your social media management with Hootsuite, the leading social media dashboard. Manage multiple networks and profiles and measure your campaign results.'
,url: 'https://hootsuite.com/dashboard'
,type: 'messaging'
},
{
id: 'zimbra'
,logo: 'zimbra.png'
,name: 'Zimbra'
,description: 'Over 500 million people rely on Zimbra and enjoy enterprise-class open source email collaboration at the lowest TCO in the industry. Discover the benefits!'
,url: '___'
,type: 'email'
,js_unread: 'function check_unread(){update_badge(appCtxt.getById(ZmFolder.ID_INBOX).numUnread)}function update_badge(a){document.title=a>0?"("+a+") "+original_title:original_title}const original_title=document.title;setInterval(check_unread,3e3);'
},
{
id: 'kaiwa'
,logo: 'kaiwa.png'
,name: 'Kaiwa'
,description: 'A modern and Open Source Web client for XMPP.'
,url: '___'
,type: 'messaging'
,js_unread: 'function check_unread() { let count=0; for (let node of document.getElementsByClassName("unread")){ if (node.innerHTML){ count += parseInt(node.innerHTML); } } update_badge(count);}function update_badge(a) { document.title = a > 0 ? "(" + a + ") " + original_title : original_title}const original_title = document.title;setInterval(check_unread, 3e3);'
},
{
id: 'movim'
,logo: 'movim.png'
,name: 'Movim'
,description: 'Movim is a decentralized social network, written in PHP and HTML5 and based on the XMPP standard protocol.'
,url: 'https://___.movim.eu/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("color dark"),b=0;for(i=0;i<a.length;i++){var c=a[i].getElementsByClassName("counter");for(ii=0;ii<c.length;ii++)parseInt(c[ii].textContent.trim())%1===0&&(b+=parseInt(c[ii].textContent.trim()))}updateBadge(b)}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);'
,custom_domain: true
},
{
id: 'pushbullet'
,logo: 'pushbullet.png'
,name: 'Pushbullet'
,description: 'Pushbullet connects your devices, making them feel like one.'
,url: 'https://www.pushbullet.com/'
,type: 'messaging'
},
{
id: 'riot'
,logo: 'riot.png'
,name: 'Riot'
,description: 'Riot is a simple and elegant collaboration environment that gathers all of your different conversations and app integrations into one single app.'
,url: 'https://riot.im/app/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("mx_RoomTile_nameContainer"),b=0;for(i=0;i<a.length;i++){var c=a[i].getElementsByClassName("mx_RoomTile_badge");for(ii=0;ii<c.length;ii++)parseInt(c[ii].textContent.trim())%1===0&&(b+=parseInt(c[ii].textContent.trim()))}updateBadge(b)}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,1e3);'
,custom_domain: true
},
{
id: 'socialcast'
,logo: 'socialcast.png'
,name: 'Socialcast'
,description: 'Socialcast is the premier enterprise social networking platform that connects people to the knowledge, ideas and resources they need to work more effectively.'
,url: 'https://___.socialcast.com/'
,type: 'messaging'
},
{
id: 'fleep'
,logo: 'fleep.png'
,name: 'Fleep'
,description: 'Fleep enables communication within and across organizations - be it your team chats, project communication or 1:1 conversations.'
,url: 'https://fleep.io/chat'
,type: 'messaging'
,js_unread: 'document.getElementsByClassName("google-login-area")[0].remove();document.getElementsByClassName("microsoft-login-area")[0].remove();'
},
{
id: 'spark'
,logo: 'spark.png'
,name: 'Cisco Spark'
,description: 'Cisco Spark is for group chat, video calling, and sharing documents with your team. It’s all backed by Cisco security and reliability.'
,url: 'https://web.ciscospark.com/'
,type: 'messaging'
},
{
id: 'drift'
,logo: 'drift.png'
,name: 'Drift'
,description: 'Drift is a messaging app that makes it easy for businesses to talk to their website visitors and customers in real-time, from anywhere.'
,url: 'https://app.drift.com/'
,type: 'messaging'
},
{
id: 'typetalk'
,logo: 'typetalk.png'
,name: 'Typetalk'
,description: 'Typetalk brings fun and ease to team discussions through instant messaging on desktop and mobile devices.'
,url: 'https://typetalk.in/signin'
,type: 'messaging'
},
{
id: 'openmailbox'
,logo: 'openmailbox.png'
,name: 'Openmailbox'
,description: 'Free mail hosting. Respect your rights and your privacy.'
,url: 'https://app.openmailbox.org/webmail/'
,type: 'email'
},
{
id: 'flock'
,logo: 'flock.png'
,name: 'Flock'
,description: 'Flock is a free enterprise tool for business communication. Packed with tons of productivity features, Flock drives efficiency and boosts speed of execution.'
,url: 'https://web.flock.co/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("unreadMessages no-unread-mentions has-unread"),b=0;for(i=0;i<a.length;i++)b+=parseInt(a[i].innerHTML.trim());updateBadge(b)}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,dont_update_unread_from_title: true
},
{
id: 'crisp'
,logo: 'crisp.png'
,name: 'Crisp'
,description: 'Connect your customers to your team.'
,url: 'https://app.crisp.im/inbox'
,type: 'messaging'
},
{
id: 'smooch'
,logo: 'smooch.png'
,name: 'Smooch'
,description: 'Unified multi-channel messaging for businesses, bots and software makers.'
,url: 'https://app.smooch.io/'
,type: 'messaging'
},
{
id: 'xing',
logo: 'xing.png',
name: 'XING',
description: 'Career-oriented social networking',
url: 'https://www.xing.com/messages/conversations',
type: 'messaging',
js_unread: '(function() { let originalTitle = document.title; function checkUnread() { let count = null; let notificationElement = document.querySelector(\'[data-update="unread_conversations"]\'); if (notificationElement && notificationElement.style.display !== \'none\') { count = parseInt(notificationElement.textContent.trim(), 10); } updateBadge(count); } function updateBadge(count) { if (count && count >= 1) { rambox.setUnreadCount(count); } else { rambox.clearUnreadCount(); } } setInterval(checkUnread, 3000); checkUnread(); })();',
dont_update_unread_from_title: true
},
{
id: 'threema',
logo: 'threema.png',
name: 'Threema',
description: 'Seriously secure messaging',
url: 'https://web.threema.ch/',
type: 'messaging',
js_unread: '(function () { let unreadCount = 0; function checkUnread() { let newUnread = 0; try { let webClientService = angular.element(document.documentElement).injector().get(\'WebClientService\'); let conversations = webClientService.conversations.conversations; conversations.forEach(function(conversation) { newUnread += conversation.unreadCount; }); } catch (e) { } if (newUnread !== unreadCount) { unreadCount = newUnread; updateBadge(unreadCount); } } function updateBadge(count) { if (count && count >= 1) { rambox.setUnreadCount(count); } else { rambox.clearUnreadCount(); } } setInterval(checkUnread, 3000); checkUnread(); })();',
dont_update_unread_from_title: true
},
{
id: 'workplace'
,logo: 'workplace.png'
,name: 'Workplace'
,description: 'Connect everyone in your company and turn ideas into action. Through group discussion, a personalised News Feed, and voice and video calling, work together and get more done. Workplace is an ad-free space, separate from your personal Facebook account.'
,url: 'https://___.facebook.com/'
,type: 'messaging'
},
{
id: 'teams'
,logo: 'teams.png'
,name: 'Teams'
,description: 'Microsoft Teams is the chat-based workspace in Office 365 that integrates all the people, content, and tools your team needs to be more engaged and effective.'
,url: 'https://teams.microsoft.com'
,type: 'messaging'
,userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
},
{
id: 'kezmo'
,logo: 'kezmo.png'
,name: 'Kezmo'
,description: 'Kezmo is an enterprise chat and collaboration tool to help teams get things done. It’s an email alternative for secure team communication.'
,url: 'https://app.kezmo.com/web/'
,type: 'messaging'
},
{
id: 'lounge'
,logo: 'lounge.png'
,name: 'The Lounge'
,description: 'Self-hosted web IRC client.'
,url: '___'
,type: 'messaging'
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("badge highlight"),b=0;for(i=0;i<a.length;i++)parseInt(a[i].textContent.trim())%1===0&&(b+=parseInt(a[i].textContent.trim()));updateBadge(b)}function updateBadge(a){a>=1?document.title="("+a+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,1e3);'
},
{
id: 'linkedin'
,logo: 'linkedin.png'
,name: 'LinkedIn Messaging'
,description: 'Manage your professional identity. Build and engage with your professional network. Access knowledge, insights and opportunities.'
,url: 'https://www.linkedin.com/messaging'
,type: 'messaging'
},
{
id: 'zyptonite'
,logo: 'zyptonite.png'
,name: 'Zyptonite'
,description: 'Zyptonite is the ultimate cyber secure communication tool for enterprise customers designed to address the need to securely communicate via voice, video, and chat, and transfer files and information across a global mobile workforce.'
,url: 'https://app.zyptonite.com/'
,type: 'messaging'
,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("z-messages"),b=0;for(i=0;i<a.length;i++)b+=parseInt(a[i].innerHTML.trim());updateBadge(b)}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,dont_update_unread_from_title: true
},
{
id: 'fastmail'
,logo: 'fastmail.png'
,name: 'FastMail'
,description: 'Secure, reliable email hosting for businesses, families and professionals. Premium email with no ads, excellent spam protection and rapid personal support.'
,url: 'https://www.fastmail.com/mail/'
,type: 'mail'
,js_unread: 'setTimeout(function(){O.WindowController.openExternal=function(a){var b=document.createElement("a");b.href=a,b.setAttribute("target","_blank"),b.click()};},3000);'
},
{
id: 'hibox'
,logo: 'hibox.png'
,name: 'Hibox'
,description: 'Hibox is a secure and private messaging platform for your business.'
,url: 'https://app.hibox.co/'
,type: 'messaging'
},
{
id: 'jandi'
,logo: 'jandi.png'
,name: 'Jandi'
,description: 'Jandi is a group-oriented enterprise messaging platform with an integrated suite of collaboration tools for workplace.'
,url: 'https://___.jandi.com/'
,type: 'messaging'
},
{
id: 'messengerpages'
,logo: 'messengerpages.png'
,name: 'Messenger for Pages'
,description: 'Chat with the people of your Facebook Page.'
,url: 'https://facebook.com/___/inbox/'
,type: 'messaging'
,js_unread: 'function remove(e){var r=document.getElementById(e);return r.parentNode.removeChild(r)}remove("pagelet_bluebar"),remove("pages_manager_top_bar_container");'
},
{
id: 'vk'
,logo: 'vk.png'
,name: 'VK Messenger'
,description: 'Simple and Easy App for Messaging on VK.'
,url: 'https://vk.com/im'
,type: 'messaging'
,js_unread: 'function checkUnread(){updateBadge(parseInt(document.getElementById("l_msg").innerText.replace(/\D+/g,"")))}function updateBadge(e){e>=1?document.title="("+e+") "+originalTitle:document.title=originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);'
},
{
id: 'mastodon'
,logo: 'mastodon.png'
,name: 'Mastodon'
,description: 'Mastodon is a free, open-source social network server. A decentralized solution to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly.'
,url: 'https://mastodon.social/auth/sign_in'
,type: 'messaging'
,custom_domain: true
,note: '<a href="https://instances.mastodon.xyz/" target="_blank">List of instances</a>'
},
{
id: 'teamworkchat'
,logo: 'teamworkchat.png'
,name: 'Teamwork Chat'
,description: 'Say goodbye to email. Take your online collaboration to the next level with Teamwork Chat and keep all team discussions in one place. Chat to your team in a fun and informal way with Teamwork Chat.'
,url: 'https://___.teamwork.com/chat'
,type: 'messaging'
,js_unread: 'function checkUnread(){updateBadge(parseInt(document.getElementsByClassName("sidebar-notification-indicator").length > 0 ? document.getElementsByClassName("sidebar-notification-indicator")[0].innerHTML : 0))}function updateBadge(a){a>=1?rambox.setUnreadCount(a):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,dont_update_unread_from_title: true
},
{
id: 'clocktweets'
,logo: 'clocktweets.png'
,name: 'ClockTweets'
,description: 'Schedule your Tweets with love. Save time and manage your social media strategy easily.'
,url: 'https://clocktweets.com/dashboard/'
,type: 'messaging'
},
{
id: 'intercom'
,logo: 'intercom.png'
,name: 'Intercom'
,description: 'Intercom makes it easy to communicate with your customers personally, at scale. Designed to feel like the messaging apps you use every day, Intercom lets you talk to consumers almost anywhere: inside your app, on your website, across social media and via email.'
,url: 'https://app.intercom.io'
,type: 'messaging'
},
{
id: 'allo'
,logo: 'allo.png'
,name: 'Allo'
,description: 'Google Allo is a smart messaging app that helps you say more and do more. Express yourself better with stickers, doodles, and HUGE emojis & text. Allo also brings you the Google Assistant.'
,url: 'https://allo.google.com/web'
,type: 'messaging'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("unreadCount"),n=0;for(i=0;i<e.length;i++){var a=parseInt(e[i].innerHTML.trim());n+=isNaN(a)?0:a}updateBadge(n)}function updateBadge(e){e&&e>=1?rambox.setUnreadCount(e):rambox.clearUnreadCount()}setInterval(checkUnread,3e3);'
,dont_update_unread_from_title: true
}
]
});

36
app/util/IconLoader.js

@ -1,36 +0,0 @@
/**
* Singleton class to handle the custom icons for special services.
*/
Ext.define('Rambox.util.IconLoader', {
singleton: true,
constructor: function(config) {
config = config || {};
/**
* Sets the icon for a specific service.
*
* @param {*} service Id of the service
* @param {*} webview Webview component of the service
*/
this.loadServiceIconUrl = function (service, webview) {
switch (service.type) {
case 'slack':
webview.executeJavaScript("(a=>window.slackDebug.activeTeam.redux.getState().teams[a].icon.image_44)(window.slackDebug.activeTeamId);")
.then(backgroundImage => {
if (backgroundImage) {
service.setTitle('<img src="'+service.icon+'" width="" style="background-color: white;border-radius: 50%;position: absolute;left: 18px;top: 17px;width: 12px;">'+service.title);
service.fireEvent('iconchange', service, backgroundImage, service.icon);
}
}).catch(err => {
console.log(err);
})
break;
default:
break;
}
};
}
});

241
app/ux/Auth0.js

@ -0,0 +1,241 @@
Ext.define('Rambox.ux.Auth0', {
singleton: true
// private
,lock: null
,auth0: null
,backupCurrent: false
,init: function() {
var me = this;
// Auth0 Config
me.lock = new Auth0Lock(auth0Cfg.clientID, auth0Cfg.domain, {
autoclose: true
,autofocus: true
,auth: {
redirect: false
,params: {scope: 'openid offline_access'}
}
,theme: {
logo: 'resources/Icon.png'
,primaryColor: '#0675A0'
}
,languageDictionary: {
title: 'Rambox Account'
}
,popupOptions: {
nodeIntegration: 'no'
}
,language: localStorage.getItem('locale-auth0') === null ? 'en' : localStorage.getItem('locale-auth0')
});
me.auth0 = new Auth0({ clientID: auth0Cfg.clientID, domain : auth0Cfg.domain });
me.defineEvents();
}
,defineEvents: function() {
var me = this;
me.lock.on("authenticated", function(authResult) {
me.lock.getProfile(authResult.idToken, function(err, profile) {
if (err) {
// Handle error
Ext.Msg.hide();
return;
}
// Display a spinner while waiting
Ext.Msg.wait(locale['app.window[29]'], locale['app.window[28]']);
// Google Analytics Event
ga_storage._trackEvent('Users', 'loggedIn');
// Set cookies to help Tooltip.io messages segmentation
Ext.util.Cookies.set('auth0', true);
// User is logged in
// Save the profile and JWT.
localStorage.setItem('profile', JSON.stringify(profile));
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('refresh_token', authResult.refreshToken);
if ( !Ext.isEmpty(profile.user_metadata) && !Ext.isEmpty(profile.user_metadata.services) && !me.backupCurrent ) {
Ext.each(profile.user_metadata.services, function(s) {
var service = Ext.create('Rambox.model.Service', s);
service.save();
Ext.getStore('Services').add(service);
});
require('electron').remote.getCurrentWindow().reload();
}
Ext.Msg.hide();
Ext.cq1('app-main').getViewModel().set('username', profile.name);
Ext.cq1('app-main').getViewModel().set('avatar', profile.picture);
});
});
}
,backupConfiguration: function(callback) {
var me = this;
Ext.Msg.wait('Saving backup...', 'Please wait...');
// Getting all services
var lastupdate = (new Date()).toJSON();
var services = [];
Ext.getStore('Services').each(function(service) {
var s = Ext.clone(service);
delete s.data.id;
delete s.data.zoomLevel;
services.push(s.data);
});
Ext.Ajax.request({
url: 'https://rambox.auth0.com/api/v2/users/'+Ext.decode(localStorage.getItem('profile')).user_id
,method: 'PATCH'
,headers: { authorization: "Bearer " + localStorage.getItem('id_token') }
,jsonData: { user_metadata: { services: services, services_lastupdate: lastupdate } }
,success: function(response) {
Ext.Msg.hide();
// Save the last update in localStorage
var profile = Ext.decode(localStorage.getItem('profile'));
if ( !profile.user_metadata ) profile.user_metadata = {};
profile.user_metadata.services_lastupdate = lastupdate;
localStorage.setItem('profile', Ext.encode(profile));
Ext.cq1('app-main').getViewModel().set('last_sync', new Date(lastupdate).toUTCString());
Ext.toast({
html: '<i class="fa fa-check fa-3x fa-pull-left" aria-hidden="true"></i> Your configuration were successfully backed up.'
,title: 'Synchronize Configuration'
,width: 300
,align: 't'
,closable: false
});
if ( Ext.isFunction(callback) ) callback.bind(me)();
}
,failure: function(response) {
if ( response.status === 401 ) return me.renewToken(me.backupConfiguration);
Ext.Msg.hide();
Ext.toast({
html: '<i class="fa fa-times fa-3x fa-pull-left" aria-hidden="true"></i> Error occurred when trying to backup your configuration.'
,title: 'Synchronize Configuration'
,width: 300
,align: 't'
,closable: false
});
if ( Ext.isFunction(callback) ) callback.bind(me)();
console.error(response);
}
});
}
,restoreConfiguration: function() {
var me = this;
me.lock.getProfile(localStorage.getItem('id_token'), function (err, profile) {
if ( err ) {
if ( err.error === 401 ) return me.renewToken(me.restoreConfiguration);
return alert('There was an error getting the profile: ' + err.message);
}
// First we remove all current services
Ext.cq1('app-main').getController().removeAllServices(false, function() {
Ext.each(profile.user_metadata.services, function(s) {
var service = Ext.create('Rambox.model.Service', s);
service.save();
Ext.getStore('Services').add(service);
});
require('electron').remote.getCurrentWindow().reload();
});
});
}
,checkConfiguration: function() {
var me = this;
me.lock.getProfile(localStorage.getItem('id_token'), function (err, profile) {
if ( err ) {
if ( err.error === 401 ) return me.renewToken(me.checkConfiguration);
return alert('There was an error getting the profile: ' + err.message);
}
if ( !profile.user_metadata ) {
Ext.toast({
html: 'You don\'t have any backup yet.'
,title: 'Synchronize Configuration'
,width: 300
,align: 't'
,closable: false
});
return;
}
if ( Math.floor(new Date(profile.user_metadata.services_lastupdate) / 1000) > Math.floor(new Date(Ext.decode(localStorage.getItem('profile')).user_metadata.services_lastupdate) / 1000) ) {
Ext.toast({
html: 'Your settings are out of date.'
,title: 'Synchronize Configuration'
,width: 300
,align: 't'
,closable: false
});
} else {
Ext.toast({
html: 'Latest backup is already applied.'
,title: 'Synchronize Configuration'
,width: 300
,align: 't'
,closable: false
});
}
});
}
,renewToken: function(callback) {
var me = this;
Ext.Ajax.request({
url: 'https://rambox.auth0.com/delegation'
,method: 'POST'
,jsonData: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer'
,client_id: auth0Cfg.clientID
,refresh_token: localStorage.getItem('refresh_token')
,api_type: 'app'
}
,success: function(response) {
var json = Ext.decode(response.responseText);
localStorage.setItem('id_token', json.id_token);
if ( Ext.isFunction(callback) ) callback.bind(me)();
}
,failure: function(response) {
console.error(response);
}
});
}
,login: function() {
var me = this;
me.lock.show();
}
,logout: function() {
var me = this;
localStorage.removeItem('profile');
localStorage.removeItem('id_token');
localStorage.removeItem('refresh_token');
// Set cookies to help Tooltip.io messages segmentation
Ext.util.Cookies.set('auth0', false);
}
});

420
app/ux/WebView.js

@ -1,7 +1,6 @@
/**
* Default config for all webviews created
*/
Ext.define('Rambox.ux.WebView',{
extend: 'Ext.panel.Panel'
,xtype: 'webview'
@ -10,7 +9,6 @@ Ext.define('Rambox.ux.WebView',{
'Rambox.util.Format'
,'Rambox.util.Notifier'
,'Rambox.util.UnreadCounter'
,'Rambox.util.IconLoader'
]
// private
@ -35,10 +33,12 @@ Ext.define('Rambox.ux.WebView',{
}
}
const prefConfig = ipc.sendSync('getConfig');
// Allow Custom sites with self certificates
//if ( me.record.get('trust') ) ipc.send('allowCertificate', me.src);
Ext.apply(me, {
items: me.webViewConstructor()
,title: prefConfig.hide_tabbar_labels ? '' : (me.record.get('tabname') ? me.record.get('name') : '')
,title: me.record.get('tabname') ? me.record.get('name') : ''
,icon: me.record.get('type') === 'custom' ? (me.record.get('logo') === '' ? 'resources/icons/custom.png' : me.record.get('logo')) : 'resources/icons/'+me.record.get('logo')
,src: me.record.get('url')
,type: me.record.get('type')
@ -123,45 +123,6 @@ Ext.define('Rambox.ux.WebView',{
]
}
}
,tbar: {
itemId: 'searchBar'
,hidden: true
,items: ['->', {
xtype: 'textfield'
,emptyText: 'Search...'
,listeners: {
scope: me
,change: me.doSearchText
,specialkey: function(field, e) {
if ( e.getKey() === e.ENTER ) return me.doSearchText(field, field.getValue(), null, null, true)
if ( e.getKey() === e.ESC ) return me.showSearchBox(false)
}
}
}, {
xtype: 'displayfield'
}, {
xtype: 'segmentedbutton'
,allowMultiple: false
,allowToggle: false
,items: [{
glyph: 'xf053@FontAwesome'
,handler: function() {
var field = this.up('toolbar').down('textfield');
me.doSearchText(field, field.getValue(), null, null, false)
}
}, {
glyph: 'xf054@FontAwesome'
,handler: function() {
var field = this.up('toolbar').down('textfield');
me.doSearchText(field, field.getValue(), null, null, true)
}
}]
}, {
xtype: 'button'
,glyph: 'xf00d@FontAwesome'
,handler: function() { me.showSearchBox(false) }
}]
}
,listeners: {
afterrender: me.onAfterRender
,beforedestroy: me.onBeforeDestroy
@ -201,7 +162,6 @@ Ext.define('Rambox.ux.WebView',{
} else {
cfg = [{
xtype: 'component'
,cls: 'webview'
,hideMode: 'offsets'
,autoRender: true
,autoShow: true
@ -209,32 +169,28 @@ Ext.define('Rambox.ux.WebView',{
tag: 'webview'
,src: me.record.get('url')
,style: 'width:100%;height:100%;visibility:visible;'
,partition: 'persist:' + me.record.get('type') + '_' + me.id.replace('tab_', '') + (localStorage.getItem('id_token') ? '_' + Ext.decode(localStorage.getItem('profile')).sub : '')
,partition: 'persist:' + me.record.get('type') + '_' + me.id.replace('tab_', '') + (localStorage.getItem('id_token') ? '_' + Ext.decode(localStorage.getItem('profile')).user_id : '')
,plugins: 'true'
,allowtransparency: 'on'
,autosize: 'on'
,webpreferences: 'nativeWindowOpen=yes, spellcheck=no, contextIsolation=no'
,allowpopups: 'on'
// ,disablewebsecurity: 'on' // Disabled because some services (Like Google Drive) dont work with this enabled
,useragent: me.getUserAgent()
//,webpreferences: 'nodeIntegration=no'
//,disablewebsecurity: 'on' // Disabled because some services (Like Google Drive) dont work with this enabled
,useragent: Ext.getStore('ServicesList').getById(me.record.get('type')).get('userAgent')
,preload: './resources/js/rambox-service-api.js'
}
}];
if ( Ext.getStore('ServicesList').getById(me.record.get('type')).get('allow_popups') ) cfg[0].autoEl.allowpopups = 'on';
}
return cfg;
}
,getUserAgent: function() {
var ua = ipc.sendSync('getConfig').user_agent ? ipc.sendSync('getConfig').user_agent : Ext.getStore('ServicesList').getById(this.record.get('type')) ? Ext.getStore('ServicesList').getById(this.record.get('type')).get('userAgent') : ''
return ua.length === 0 ? window.clientInformation.userAgent.replace(/Rambox\/([0-9]\.?)+\s/ig,'').replace(/Electron\/([0-9]\.?)+\s/ig,'') : ua;
}
,statusBarConstructor: function(floating) {
var me = this;
return {
xtype: 'statusbar'
,id: me.id+'statusbar'
,hidden: !me.record.get('statusbar')
,keep: me.record.get('statusbar')
,y: floating ? '-18px' : 'auto'
@ -271,26 +227,20 @@ Ext.define('Rambox.ux.WebView',{
if ( !me.record.get('enabled') ) return;
var webview = me.getWebView();
me.errorCodeLog = []
var webview = me.down('component').el.dom;
// Google Analytics Event
ga_storage._trackEvent('Services', 'load', me.type, 1, true);
// Notifications in Webview
me.setNotifications(localStorage.getItem('locked') || JSON.parse(localStorage.getItem('dontDisturb')) ? false : me.record.get('notifications'));
require('electron').remote.session.fromPartition('persist:' + me.record.get('type') + '_' + me.id.replace('tab_', '') + (localStorage.getItem('id_token') ? '_' + Ext.decode(localStorage.getItem('profile')).sub : '')).webRequest.onBeforeSendHeaders((details, callback) => {
const change = details.url.match(/^https:\/\/accounts\.google\.com(\/|$)/);
if ( change ) details.requestHeaders['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0';
callback({ cancel: false, requestHeaders: details.requestHeaders });
});
// Show and hide spinner when is loading
webview.addEventListener("did-start-loading", function() {
console.info('Start loading...', me.src);
if ( !me.down('statusbar').closed || !me.down('statusbar').keep ) me.down('statusbar').show();
me.down('statusbar').showBusy();
});
webview.addEventListener("did-stop-loading", function() {
me.down('statusbar').clearStatus({useDefaults: true});
if ( !me.down('statusbar').keep ) me.down('statusbar').hide();
@ -301,95 +251,98 @@ Ext.define('Rambox.ux.WebView',{
// Apply saved zoom level
webview.setZoomLevel(me.record.get('zoomLevel'));
// Fix cursor sometimes dissapear
let currentTab = Ext.cq1('app-main').getActiveTab();
if ( currentTab.id === me.id ) {
webview.blur();
webview.focus();
}
// Set special icon for some service (like Slack)
Rambox.util.IconLoader.loadServiceIconUrl(me, webview);
});
// On search text
webview.addEventListener('found-in-page', function(e) {
me.onSearchText(e.result)
});
// On search text
webview.addEventListener('did-fail-load', function(e) {
console.info('The service fail at loading', me.src, e);
if ( me.record.get('disableAutoReloadOnFail') || !e.isMainFrame ) return
me.errorCodeLog.push(e.errorCode)
var attempt = me.errorCodeLog.filter(function(code) { return code === e.errorCode });
// Error codes: https://cs.chromium.org/chromium/src/net/base/net_error_list.h
var msg = []
msg[-2] = 'NET error: failed.'
msg[-3] = 'An operation was aborted (due to user action)'
msg[-7] = 'Connection timeout.'
msg[-21] = 'Network change.'
msg[-100] = 'The connection was reset. Check your internet connection.'
msg[-101] = 'The connection was reset. Check your internet connection.'
msg[-105] = 'Name not resolved. Check your internet connection.'
msg[-106] = 'There is no active internet connection.'
msg[-118] = 'Connection timed out. Check your internet connection.'
msg[-130] = 'Proxy connection failed. Please, check the proxy configuration.'
msg[-300] = 'The URL is invalid.'
msg[-324] = 'Empty response. Check your internet connection.'
switch ( e.errorCode ) {
case 0:
break
case -3: // An operation was aborted (due to user action) I think that gmail an other pages that use iframes stop some of them making this error fired
if ( attempt.length <= 4 ) return
setTimeout(() => me.reloadService(me), 200);
me.errorCodeLog = []
// Open links in default browser
webview.addEventListener('new-window', function(e) {
switch ( me.type ) {
case 'skype':
// hack to fix multiple browser tabs on Skype link click, re #11
if ( e.url.match('https:\/\/web.skype.com\/..\/undefined') ) {
e.preventDefault();
return;
} else if ( e.url.indexOf('imgpsh_fullsize') >= 0 ) {
ipc.send('image:download', e.url, e.target.partition);
e.preventDefault();
return;
}
break;
case -2:
case -7:
case -21:
case -118:
case -324:
case -100:
case -101:
case -105:
attempt.length > 4 ? me.onFailLoad(msg[e.errorCode]) : setTimeout(() => me.reloadService(me), 2000);
case 'hangouts':
e.preventDefault();
if ( e.url.indexOf('plus.google.com/u/0/photos/albums') >= 0 ) {
ipc.send('image:popup', e.url, e.target.partition);
return;
} else if ( e.url.indexOf('https://hangouts.google.com/hangouts/_/CONVERSATION/') >= 0 ) {
me.add({
xtype: 'window'
,title: 'Video Call'
,width: '80%'
,height: '80%'
,maximizable: true
,resizable: true
,draggable: true
,collapsible: true
,items: {
xtype: 'component'
,hideMode: 'offsets'
,autoRender: true
,autoShow: true
,autoEl: {
tag: 'webview'
,src: e.url
,style: 'width:100%;height:100%;'
,partition: 'persist:' + me.record.get('type') + '_' + me.id.replace('tab_', '') + (localStorage.getItem('id_token') ? '_' + Ext.decode(localStorage.getItem('profile')).user_id : '')
,useragent: Ext.getStore('ServicesList').getById(me.record.get('type')).get('userAgent')
}
}
}).show();
return;
}
break;
case -106:
me.onFailLoad(msg[e.errorCode])
case 'slack':
if ( e.url.indexOf('slack.com/call/') >= 0 ) {
me.add({
xtype: 'window'
,title: e.options.title
,width: e.options.width
,height: e.options.height
,maximizable: true
,resizable: true
,draggable: true
,collapsible: true
,items: {
xtype: 'component'
,hideMode: 'offsets'
,autoRender: true
,autoShow: true
,autoEl: {
tag: 'webview'
,src: e.url
,style: 'width:100%;height:100%;'
,partition: e.options.webPreferences.partition
,useragent: Ext.getStore('ServicesList').getById(me.record.get('type')).get('userAgent')
}
}
}).show();
e.preventDefault();
return;
}
break;
case -130:
// Could not create a connection to the proxy server. An error occurred
// either in resolving its name, or in connecting a socket to it.
// Note that this does NOT include failures during the actual "CONNECT" method
// of an HTTP proxy.
case -300:
attempt.length > 4 ? me.onFailLoad(msg[e.errorCode]) : me.reloadService(me);
default:
break;
}
});
// Open links in default browser
webview.addEventListener('new-window', function(e) {
e.preventDefault();
const { URL } = require('url');
const url = new URL(e.url);
const protocol = url.protocol;
// Block some Deep links to prevent that open its app (Ex: Slack)
if ( ['slack:'].includes(protocol) ) return;
// Allow Deep links
if ( !['http:', 'https:', 'about:'].includes(protocol) ) return require('electron').shell.openExternal(url.href);
const protocol = require('url').parse(e.url).protocol;
if (protocol === 'http:' || protocol === 'https:' || protocol === 'mailto:') {
e.preventDefault();
require('electron').shell.openExternal(e.url);
}
});
webview.addEventListener('will-navigate', function(e, url) {
e.preventDefault();
});
let eventsOnDom = false;
webview.addEventListener("dom-ready", function(e) {
// Mute Webview
if ( me.record.get('muted') || localStorage.getItem('locked') || JSON.parse(localStorage.getItem('dontDisturb')) ) me.setAudioMuted(true, true);
@ -397,7 +350,7 @@ Ext.define('Rambox.ux.WebView',{
var js_inject = '';
// Injected code to detect new messages
if ( me.record ) {
var js_unread = Ext.getStore('ServicesList').getById(me.record.get('type')) ? Ext.getStore('ServicesList').getById(me.record.get('type')).get('js_unread') : '' ;
var js_unread = Ext.getStore('ServicesList').getById(me.record.get('type')).get('js_unread');
js_unread = js_unread + me.record.get('js_unread');
if ( js_unread !== '' ) {
console.groupCollapsed(me.record.get('type').toUpperCase() + ' - JS Injected to Detect New Messages');
@ -408,7 +361,7 @@ Ext.define('Rambox.ux.WebView',{
}
// Prevent Title blinking (some services have) and only allow when the title have an unread regex match: "(3) Title"
if ( Ext.getStore('ServicesList').getById(me.record.get('type')) ? Ext.getStore('ServicesList').getById(me.record.get('type')).get('titleBlink') : false ) {
if ( Ext.getStore('ServicesList').getById(me.record.get('type')).get('titleBlink') ) {
var js_preventBlink = 'var originalTitle=document.title;Object.defineProperty(document,"title",{configurable:!0,set:function(a){null===a.match(new RegExp("[(]([0-9•]+)[)][ ](.*)","g"))&&a!==originalTitle||(document.getElementsByTagName("title")[0].innerHTML=a)},get:function(){return document.getElementsByTagName("title")[0].innerHTML}});';
console.log(js_preventBlink);
js_inject += js_preventBlink;
@ -420,8 +373,8 @@ Ext.define('Rambox.ux.WebView',{
js_inject += 'document.body.scrollTop=0;';
// Handles Certificate Errors
require('electron').remote.webContents.fromId(webview.getWebContentsId()).on('certificate-error', function(event, url, error, certificate, callback) {
if (me.record.get('trust')) {
webview.getWebContents().on('certificate-error', function(event, url, error, certificate, callback) {
if ( me.record.get('trust') ) {
event.preventDefault();
callback(true);
} else {
@ -431,54 +384,12 @@ Ext.define('Rambox.ux.WebView',{
me.down('statusbar').keep = true;
me.down('statusbar').show();
me.down('statusbar').setStatus({
text: '<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Certification Warning',
text: '<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Certification Warning'
});
me.down('statusbar').down('button').show();
});
if (!eventsOnDom) {
require('electron').remote.webContents.fromId(webview.getWebContentsId()).on('before-input-event', (event, input) => {
if (input.type !== 'keyDown') return;
var modifiers = [];
if (input.shift) modifiers.push('shift');
if (input.control) modifiers.push('control');
if (input.alt) modifiers.push('alt');
if (input.meta) modifiers.push('meta');
if (input.isAutoRepeat) modifiers.push('isAutoRepeat');
if (input.key === 'Tab' && !(modifiers && modifiers.length)) return;
// Maps special keys to fire the correct event in Mac OS
if (require('electron').remote.process.platform === 'darwin') {
var keys = [];
keys['ƒ'] = 'f'; // Search
keys[' '] = 'l'; // Lock
keys['∂'] = 'd'; // DND
input.key = keys[input.key] ? keys[input.key] : input.key;
}
if (
input.key === 'F11' ||
input.key === 'a' ||
input.key === 'A' ||
input.key === 'F12' ||
input.key === 'q' ||
(input.key === 'F1' && modifiers.includes('control'))
)
return;
require('electron').remote.getCurrentWebContents().sendInputEvent({
type: input.type,
keyCode: input.key,
modifiers: modifiers,
});
});
eventsOnDom = true;
Rambox.app.config.googleURLs.forEach((loginURL) => { if ( webview.getURL().indexOf(loginURL) > -1 ) webview.reload() })
}
webview.executeJavaScript(js_inject).then(result => {} ).catch(err => { console.log(err) })
webview.executeJavaScript(js_inject);
});
webview.addEventListener('ipc-message', function(event) {
@ -494,6 +405,7 @@ Ext.define('Rambox.ux.WebView',{
showWindowAndActivateTab(event);
break;
}
/**
* Handles 'rambox.clearUnreadCount' messages.
* Clears the unread count.
@ -506,15 +418,14 @@ Ext.define('Rambox.ux.WebView',{
/**
* Handles 'rambox.setUnreadCount' messages.
* Sets the badge text if the event contains an integer or a '•' (indicating non-zero but unknown number of unreads) as first argument.
* Sets the badge text if the event contains an integer as first argument.
*
* @param event
*/
function handleSetUnreadCount(event) {
if (Array.isArray(event.args) === true && event.args.length > 0) {
var count = event.args[0];
if (count === parseInt(count, 10) || "•" === count) {
if ( count === 999999 ) count = "•";
if (count === parseInt(count, 10)) {
me.setUnreadCount(count);
}
}
@ -522,18 +433,14 @@ Ext.define('Rambox.ux.WebView',{
function showWindowAndActivateTab(event) {
require('electron').remote.getCurrentWindow().show();
var tabPanel = Ext.cq1('app-main');
// Temp fix missing cursor after upgrade to electron 3.x +
tabPanel.setActiveTab(me);
tabPanel.getActiveTab().getWebView().blur();
tabPanel.getActiveTab().getWebView().focus();
Ext.cq1('app-main').setActiveTab(me);
}
});
/**
* Register page title update event listener only for services that don't specify a js_unread
* Register page title update event listener only for services that don't prevent it by setting 'dont_update_unread_from_title' to true.
*/
if ( Ext.getStore('ServicesList').getById(me.record.get('type')) ? Ext.getStore('ServicesList').getById(me.record.get('type')).get('js_unread') === '' : false && me.record.get('js_unread') === '' ) {
if (Ext.getStore('ServicesList').getById(me.record.get('type')).get('dont_update_unread_from_title') !== true) {
webview.addEventListener("page-title-updated", function(e) {
var count = e.title.match(/\(([^)]+)\)/); // Get text between (...)
count = count ? count[1] : '0';
@ -544,7 +451,7 @@ Ext.define('Rambox.ux.WebView',{
});
}
webview.addEventListener('did-navigate', function( e ) {
webview.addEventListener('did-get-redirect-request', function( e ) {
if ( e.isMainFrame && me.record.get('type') === 'tweetdeck' ) Ext.defer(function() { webview.loadURL(e.newURL); }, 1000); // Applied a defer because sometimes is not redirecting. TweetDeck 2FA is an example.
});
@ -582,9 +489,12 @@ Ext.define('Rambox.ux.WebView',{
*/
,doManualNotification: function(count) {
var me = this;
var manualNotifications = Ext.getStore('ServicesList').getById(me.type) ? Ext.getStore('ServicesList').getById(me.type).get('manual_notifications') : false;
if ( manualNotifications && me.currentUnreadCount < count && me.record.get('notifications') && !JSON.parse(localStorage.getItem('dontDisturb'))) {
Rambox.util.Notifier.dispatchNotification(me, count);
if (Ext.getStore('ServicesList').getById(me.type).get('manual_notifications') &&
me.currentUnreadCount < count &&
me.record.get('notifications') &&
!JSON.parse(localStorage.getItem('dontDisturb'))) {
Rambox.util.Notifier.dispatchNotification(me, count);
}
me.currentUnreadCount = count;
@ -617,7 +527,7 @@ Ext.define('Rambox.ux.WebView',{
,reloadService: function(btn) {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
if ( me.record.get('enabled') ) {
me.clearUnreadCounter();
@ -625,62 +535,16 @@ Ext.define('Rambox.ux.WebView',{
}
}
,onFailLoad: function(v) {
let me = this
me.errorCodeLog = []
setTimeout(() => Ext.getCmp(me.id+'statusbar').setStatus({ text: '<i class="fa fa-warning fa-fw" aria-hidden="true"></i> The service failed at loading, Error: '+ v }), 1000);
}
,showSearchBox: function(v) {
var me = this;
if ( !me.record.get('enabled') ) return;
var webview = me.getWebView();
webview.stopFindInPage('keepSelection');
if ( v ) {
me.down('#searchBar').show();
setTimeout(() => { me.down('#searchBar textfield').focus() }, 100)
} else {
me.down('#searchBar').hide();
me.down('#searchBar textfield').setValue('');
}
me.down('#searchBar displayfield').setValue('');
}
,doSearchText: function(field, newValue, oldValue, eOpts, forward = true) {
var me = this;
var webview = me.getWebView();
if ( newValue === '' ) {
webview.stopFindInPage('clearSelection');
me.down('#searchBar displayfield').setValue('');
return;
}
webview.findInPage(newValue, {
forward: forward,
findNext: false,
matchCase: false
})
}
,onSearchText: function( result ) {
var me = this;
me.down('#searchBar displayfield').setValue(result.activeMatchOrdinal+ '/' + result.matches);
}
,toggleDevTools: function(btn) {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
if ( me.record.get('enabled') ) webview.isDevToolsOpened() ? webview.closeDevTools() : webview.openDevTools();
}
,setURL: function(url) {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
me.src = url;
@ -689,7 +553,7 @@ Ext.define('Rambox.ux.WebView',{
,setAudioMuted: function(muted, calledFromDisturb) {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
me.muted = muted;
@ -709,7 +573,7 @@ Ext.define('Rambox.ux.WebView',{
,setStatusBar: function(keep) {
var me = this;
me.removeDocked(me.down('statusbar'), true);
me.down('statusbar').destroy();
if ( keep ) {
me.addDocked(me.statusBarConstructor(false));
@ -721,7 +585,7 @@ Ext.define('Rambox.ux.WebView',{
,setNotifications: function(notification, calledFromDisturb) {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
me.notifications = notification;
@ -750,47 +614,43 @@ Ext.define('Rambox.ux.WebView',{
,goBack: function() {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
if ( me.record.get('enabled') ) webview.goBack();
}
,goForward: function() {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
if ( me.record.get('enabled') ) webview.goForward();
}
,zoomIn: function() {
if ( this.timeout ) clearTimeout( this.timeout );
this.timeout = setTimeout(() => {
var me = this;
var webview = me.getWebView();
me.zoomLevel = me.zoomLevel + 0.25;
if ( me.record.get('enabled') ) {
webview.setZoomLevel(me.zoomLevel);
me.record.set('zoomLevel', me.zoomLevel);
}
}, 100);
var me = this;
var webview = me.down('component').el.dom;
me.zoomLevel = me.zoomLevel + 0.25;
if ( me.record.get('enabled') ) {
webview.setZoomLevel(me.zoomLevel);
me.record.set('zoomLevel', me.zoomLevel);
}
}
,zoomOut: function() {
if ( this.timeout ) clearTimeout( this.timeout );
this.timeout = setTimeout(() => {
var me = this;
var webview = me.getWebView();
me.zoomLevel = me.zoomLevel - 0.25;
if ( me.record.get('enabled') ) {
webview.setZoomLevel(me.zoomLevel);
me.record.set('zoomLevel', me.zoomLevel);
}
}, 100);
var me = this;
var webview = me.down('component').el.dom;
me.zoomLevel = me.zoomLevel - 0.25;
if ( me.record.get('enabled') ) {
webview.setZoomLevel(me.zoomLevel);
me.record.set('zoomLevel', me.zoomLevel);
}
}
,resetZoom: function() {
var me = this;
var webview = me.getWebView();
var webview = me.down('component').el.dom;
me.zoomLevel = 0;
if ( me.record.get('enabled') ) {
@ -798,12 +658,4 @@ Ext.define('Rambox.ux.WebView',{
me.record.set('zoomLevel', me.zoomLevel);
}
}
,getWebView: function() {
if ( this.record.get('enabled') ) {
return this.down('component[cls=webview]').el.dom;
} else {
return false;
}
}
});

35
app/view/add/Add.js

@ -61,19 +61,13 @@ Ext.define('Rambox.view.add.Add',{
,{
xtype: 'textfield'
,name: 'url'
,value: me.edit && me.service.get('url').indexOf('___') >= 0 ? me.record.get('url').replace(me.service.get('url').split('___')[0], '').replace(me.service.get('url').split('___')[1], '').endsWith('/') ? me.record.get('url').replace(me.service.get('url').split('___')[0], '').replace(me.service.get('url').split('___')[1], '').slice(0, -1) : me.record.get('url').replace(me.service.get('url').split('___')[0], '').replace(me.service.get('url').split('___')[1], '') : (me.record.get('url').indexOf('___') === -1 ? me.record.get('url') : '')
,value: me.edit && me.service.get('url').indexOf('___') >= 0 ? me.record.get('url').replace(me.service.get('url').split('___')[0], '').replace(me.service.get('url').split('___')[1], '') : (me.record.get('url').indexOf('___') === -1 ? me.record.get('url') : '')
,readOnly: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? true : me.service.get('url').indexOf('___') === -1 && !me.service.get('custom_domain')) : me.record.get('url').indexOf('___') === -1 && me.record.get('custom_domain')
,allowBlank: false
,submitEmptyText: false
,emptyText: me.record.get('url') === '___' ? 'https://' : ''
,validator: function(v) {
if ( !me.edit ? me.record.get('url') !== '___' : me.service.get('url').indexOf('https://___') === 0 ) return true
if ( v.match(/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i) === null && v.match(/^https:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null && v.match(/^http:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null ) return false;
return true;
}
,listeners: {
specialkey: 'onEnter'
}
,emptyText: me.record.get('url') === '___' ? 'http://' : ''
,vtype: me.record.get('url') === '___' ? 'url' : ''
,listeners: { specialkey: 'onEnter' }
,flex: 1
}
,{
@ -86,12 +80,12 @@ Ext.define('Rambox.view.add.Add',{
items: [
{
text: me.edit ? (me.service.get('url').indexOf('___') === -1 ? 'Official Server' : Ext.String.endsWith(me.service.get('url'), '/') ? me.service.get('url').split('___')[1].slice(0, -1) : me.service.get('url').split('___')[1]) : (me.record.get('url').indexOf('___') === -1 ? 'Official Server' : Ext.String.endsWith(me.record.get('url'), '/') ? me.record.get('url').split('___')[1].slice(0, -1) : me.record.get('url').split('___')[1])
,checked: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? true : Ext.String.endsWith(me.record.get('url').endsWith('/') ? me.record.get('url').slice(0, -1) : me.record.get('url'), me.service.get('url').split('___')[1])) : true
,checked: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? true : Ext.String.endsWith(me.record.get('url'), me.service.get('url').split('___')[1])) : true
,disabled: me.edit ? me.service.get('url') === '___' : me.record.get('url') === '___'
}
,{
text: 'Custom Server'
,checked: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? false : !Ext.String.endsWith(me.record.get('url').endsWith('/') ? me.record.get('url').slice(0, -1) : me.record.get('url'), me.service.get('url').split('___')[1])) : false
,checked: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? false : !Ext.String.endsWith(me.record.get('url'), me.service.get('url').split('___')[1])) : false
,custom: true
,disabled: me.edit ? !me.service.get('custom_domain') : !me.record.get('custom_domain')
}
@ -103,7 +97,7 @@ Ext.define('Rambox.view.add.Add',{
}
,changeHandler: function(cycleBtn, activeItem) {
Ext.apply(cycleBtn.previousSibling(), {
emptyText: activeItem.custom ? 'https://' : ' '
emptyText: activeItem.custom ? 'http://' : ' '
,vtype: activeItem.custom ? 'url' : ''
});
cycleBtn.previousSibling().applyEmptyText();
@ -126,14 +120,14 @@ Ext.define('Rambox.view.add.Add',{
,{
xtype: 'hiddenfield'
,name: 'cycleValue'
,value: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? 1 : (!Ext.String.endsWith(me.record.get('url').endsWith('/') ? me.record.get('url').slice(0, -1) : me.record.get('url'), me.service.get('url').split('___')[1]) ? 2 : 1)) : 1
,value: me.edit ? (me.service.get('custom_domain') && me.service.get('url') === me.record.get('url') ? 1 : (!Ext.String.endsWith(me.record.get('url'), me.service.get('url').split('___')[1]) ? 2 : 1)) : 1
}
]
}
,{
xtype: 'textfield'
,fieldLabel: locale['app.window[18]']
,emptyText: 'https://url.com/image.png'
,emptyText: 'http://url.com/image.png'
,name: 'logo'
,vtype: me.record.get('type') === 'custom' ? 'url' : ''
,value: me.record.get('type') === 'custom' ? (me.edit ? me.record.get('logo') : '') : me.record.get('logo')
@ -192,21 +186,12 @@ Ext.define('Rambox.view.add.Add',{
,uncheckedValue: false
,inputValue: true
}
,{
xtype: 'checkbox'
,boxLabel: 'Disable auto-reload on fail'
,name: 'disableAutoReloadOnFail'
,hidden: false
,checked: me.edit ? me.record.get('disableAutoReloadOnFail') : false
,uncheckedValue: false
,inputValue: true
}
,{
xtype: 'checkbox'
,boxLabel: locale['app.window[19]']
,name: 'trust'
,hidden: me.record.get('type') !== 'custom'
,checked: me.edit ? me.record.get('trust') : false
,checked: me.edit ? me.record.get('trust') : true
,uncheckedValue: false
,inputValue: true
}

1
app/view/add/AddController.js

@ -40,7 +40,6 @@ Ext.define('Rambox.view.add.AddController', {
,includeInGlobalUnreadCounter: formValues.includeInGlobalUnreadCounter
,trust: formValues.trust
,js_unread: formValues.js_unread
,disableAutoReloadOnFail: formValues.disableAutoReloadOnFail
});
var view = Ext.getCmp('tab_'+win.record.get('id'));

149
app/view/main/Main.js

@ -25,10 +25,6 @@ Ext.define('Rambox.view.main.Main', {
,autoRender: true
,autoShow: true
,deferredRender: false
,tabBar: {
id: 'mainTabBar'
,cls: JSON.parse(localStorage.getItem('dontDisturb')) ? 'dontdisturb' : ''
}
,items: [
{
icon: 'resources/IconTray@2x.png'
@ -43,7 +39,7 @@ Ext.define('Rambox.view.main.Main', {
xtype: 'panel'
,title: locale['app.main[0]']
,margin: '0 5 0 0'
,flex: 2
,flex: 1
,header: { height: 50 }
,tools: [
{
@ -141,15 +137,12 @@ Ext.define('Rambox.view.main.Main', {
,baseCls: ''
,tooltip: locale['app.main[10]']
,handler: 'removeAllServices'
,bind: {
disabled: '{emptyServices}'
}
}
]
,columns: [
{
xtype: 'templatecolumn'
,width: 52
,width: 50
,variableRowHeight: true
,tpl: '<img src="{[ values.type !== \"custom\" ? \"resources/icons/\"+values.logo : (values.logo == \"\" ? \"resources/icons/custom.png\" : values.logo) ]}" data-qtip="{type:capitalize}" width="32" style="{[ values.enabled ? \"-webkit-filter: grayscale(0)\" : \"-webkit-filter: grayscale(1)\" ]}" />'
}
@ -234,9 +227,9 @@ Ext.define('Rambox.view.main.Main', {
,overflowHandler: 'menu'
,items: [
{
glyph: JSON.parse(localStorage.getItem('dontDisturb')) ? 'xf1f7@FontAwesome' : 'xf0f3@FontAwesome'
glyph: 'xf1f7@FontAwesome'
,text: locale['app.main[16]']+': '+(JSON.parse(localStorage.getItem('dontDisturb')) ? locale['app.window[20]'] : locale['app.window[21]'])
,tooltip: locale['app.main[17]']+'<br/><b>'+locale['app.main[18]']+(require('electron').remote.process.platform === 'darwin' ? ': Cmd + Alt + D</b>' : ': Alt + Shift + D</b>')
,tooltip: locale['app.main[17]']+'<br/><b>'+locale['app.main[18]']+': F1</b>'
,enableToggle: true
,handler: 'dontDisturb'
,reference: 'disturbBtn'
@ -246,11 +239,77 @@ Ext.define('Rambox.view.main.Main', {
,{
glyph: 'xf023@FontAwesome'
,text: locale['app.main[19]']
,tooltip: locale['app.main[20]']+'<br/><b>'+locale['app.main[18]']+(require('electron').remote.process.platform === 'darwin' ? ': Cmd + Alt + L</b>' : ': Alt + Shift + L</b>')
,tooltip: locale['app.main[20]']+'<br/><b>'+locale['app.main[18]']+': F2</b>'
,handler: 'lockRambox'
,id: 'lockRamboxBtn'
}
,'->'
,{
xtype: 'image'
,id: 'avatar'
,bind: {
src: '{avatar}'
,hidden: '{!avatar}'
}
,width: 30
,height: 30
,style: 'border-radius: 50%;border:2px solid #d8d8d8;'
}
,{
id: 'usernameBtn'
,bind: {
text: '{username}'
,hidden: '{!username}'
}
,menu: [
{
text: 'Synchronize Configuration'
,glyph: 'xf0c2@FontAwesome'
,menu: [
{
xtype: 'label'
,bind: {
html: '<b class="menu-title">Last Sync: {last_sync}</b>'
}
}
,{
text: 'Backup'
,glyph: 'xf0ee@FontAwesome'
,scope: Rambox.ux.Auth0
,handler: Rambox.ux.Auth0.backupConfiguration
}
,{
text: 'Restore'
,glyph: 'xf0ed@FontAwesome'
,scope: Rambox.ux.Auth0
,handler: Rambox.ux.Auth0.restoreConfiguration
}
,{
text: 'Check for updated backup'
,glyph: 'xf021@FontAwesome'
,scope: Rambox.ux.Auth0
,handler: Rambox.ux.Auth0.checkConfiguration
}
]
}
,'-'
,{
text: locale['app.main[21]']
,glyph: 'xf08b@FontAwesome'
,handler: 'logout'
}
]
}
,{
text: locale['app.main[22]']
,icon: 'resources/auth0.png'
,id: 'loginBtn'
,tooltip: locale['app.main[23]']+'<br /><br /><i>'+locale['app.main[24]']+' Auth0 (http://auth0.com)</i>'
,bind: {
hidden: '{username}'
}
,handler: 'login'
}
,{
tooltip: locale['preferences[0]']
,glyph: 'xf013@FontAwesome'
@ -258,23 +317,56 @@ Ext.define('Rambox.view.main.Main', {
}
]
}
,bbar: {
xtype: 'toolbar'
,cls: 'deprecation'
,items: [
'<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> <b>This version of Rambox is no longer supported.</b> We highly recommend that you update to the new version of Rambox which has a free plan with all the features you already use and much more!'
,'->'
,{
xtype: 'button'
,text: 'Migrate now'
,handler: function(btn) {
btn.setText('Downloading...');
btn.setDisabled(true);
Rambox.app.checkUpdate();
,bbar: [
{
xtype: 'segmentedbutton'
,allowToggle: false
,items: [
{
text: '<b>Help us</b> with'
,pressed: true
}
}
]
}
,{
text: locale['app.main[25]']
,glyph: 'xf21e@FontAwesome'
,handler: 'showDonate'
}
,{
text: 'Translation'
,glyph: 'xf0ac@FontAwesome'
,href: 'https://crowdin.com/project/rambox/invite'
}
]
}
,'->'
,{
xtype: 'label'
,html: '<span class="fa fa-code" style="color:black;"></span> '+locale['app.main[26]']+' <span class="fa fa-heart" style="color:red;"></span> '+locale['app.main[27]'].replace('Argentina', '<img src="resources/flag.png" alt="Argentina" data-qtip="Argentina" />')
}
,'->'
,{
xtype: 'segmentedbutton'
,allowToggle: false
,items: [
{
text: '<b>Follow us</b>'
,pressed: true
}
,{
glyph: 'xf082@FontAwesome'
,href: 'https://www.facebook.com/ramboxapp'
}
,{
glyph: 'xf099@FontAwesome'
,href: 'https://www.twitter.com/ramboxapp'
}
,{
glyph: 'xf09b@FontAwesome'
,href: 'https://www.github.com/saenzramiro/rambox'
}
]
}
]
}
,{ id: 'tbfill', tabConfig : { xtype : 'tbfill' } }
]
@ -284,6 +376,5 @@ Ext.define('Rambox.view.main.Main', {
,add: 'updatePositions'
,remove: 'updatePositions'
,childmove: 'updatePositions'
,boxready: 'initialize'
}
});

202
app/view/main/MainController.js

@ -3,27 +3,12 @@ Ext.define('Rambox.view.main.MainController', {
,alias: 'controller.main'
,initialize: function( tabPanel ) {
const config = ipc.sendSync('getConfig');
tabPanel.setTabPosition(config.tabbar_location);
tabPanel.setTabRotation(0);
var reorderer = tabPanel.plugins.find(function(plugin) { return plugin.ptype == "tabreorderer"});
if ( reorderer !== undefined ) {
const names = reorderer.container.getLayout().names;
reorderer.dd.dim = names.width;
reorderer.dd.startAttr = names.beforeX;
reorderer.dd.endAttr = names.afterX;
}
}
// Make focus on webview every time the user change tabs, to enable the autofocus in websites
,onTabChange: function( tabPanel, newTab, oldTab ) {
var me = this;
localStorage.setItem('last_active_service', newTab.id);
// Set Google Analytics event
ga_storage._trackPageview('/index.html', 'main');
if ( newTab.id === 'ramboxTab' ) {
if ( Rambox.app.getTotalNotifications() > 0 ) {
@ -39,13 +24,7 @@ Ext.define('Rambox.view.main.MainController', {
}
var webview = newTab.down('component').el.dom;
setTimeout(function () {
if ( webview ) {
tabPanel.getActiveTab().getWebView().blur();
tabPanel.getActiveTab().getWebView().focus();
}
}, 300);
if ( webview ) webview.focus();
// Update the main window so it includes the active tab title.
if ( Rambox.app.getTotalNotifications() > 0 ) {
@ -61,20 +40,14 @@ Ext.define('Rambox.view.main.MainController', {
console.log('Updating Tabs positions...');
var store = Ext.getStore('Services');
var align = 'left';
store.suspendEvent('remove');
Ext.each(tabPanel.items.items, function(t, i) {
if ( t.id !== 'ramboxTab' && t.id !== 'tbfill' && t.record.get('enabled') ) {
var rec = store.getById(t.record.get('id'));
if ( align === 'right' ) i--;
rec.set('align', align);
if ( rec.get('align') === 'right' ) i--;
rec.set('position', i);
rec.save();
}
else if ( t.id === 'tbfill' ) {
align = 'right';
}
});
store.load();
@ -96,7 +69,7 @@ Ext.define('Rambox.view.main.MainController', {
Ext.getCmp('tab_'+e.record.get('id')).setTitle(e.record.get('name'));
}
,onEnableDisableService: function(cc, rowIndex, checked, obj, hideTab) {
,onEnableDisableService: function(cc, rowIndex, checked) {
var rec = Ext.getStore('Services').getAt(rowIndex);
if ( !checked ) {
@ -114,10 +87,8 @@ Ext.define('Rambox.view.main.MainController', {
,displayTabUnreadCounter: rec.get('displayTabUnreadCounter')
,enabled: rec.get('enabled')
,record: rec
,useragent: ipc.sendSync('getConfig').user_agent
,hidden: hideTab
,tabConfig: {
service: rec
service: rec
}
});
}
@ -129,102 +100,63 @@ Ext.define('Rambox.view.main.MainController', {
});
}
,removeServiceFn: function(serviceId, total, actual, callback) {
var me = this;
,removeServiceFn: function(serviceId) {
if ( !serviceId ) return false;
// Get Tab
var tab = Ext.getCmp('tab_'+serviceId);
// Get Record
var rec = Ext.getStore('Services').getById(serviceId);
if ( !rec.get('enabled') ) {
rec.set('enabled', true);
me.onEnableDisableService(null, Ext.getStore('Services').indexOf(rec), true, null, true);
// Get Tab
var tab = Ext.getCmp('tab_'+serviceId);
// Clear all trash data
const webview = tab.getWebView();
webview.addEventListener("did-start-loading", function() {
clearData(webview, tab);
});
} else {
// Get Tab
var tab = Ext.getCmp('tab_'+serviceId);
// Clear all trash data
const webview = tab.getWebView();
clearData(webview, tab);
// Clear all trash data
if ( rec.get('enabled') && tab.down('component').el ) {
tab.down('component').el.dom.getWebContents().session.clearCache(Ext.emptyFn);
tab.down('component').el.dom.getWebContents().session.clearStorageData({}, Ext.emptyFn);
}
const config = ipc.sendSync('getConfig');
if ( config.default_service === rec.get('id') ) ipc.send('setConfig', Ext.apply(config, { default_service: 'ramboxTab' }));
function clearData(webview, tab) {
const currentWebView = require("electron").remote.webContents.fromId(
webview.getWebContentsId()
);
currentWebView.clearHistory();
currentWebView.session.flushStorageData();
currentWebView.session.clearCache().then(() => {
currentWebView.session.clearStorageData().then(() => {
currentWebView.session.cookies.flushStore().then(() => {
// Remove record from localStorage
Ext.getStore('Services').remove(rec);
// Close tab
tab.close();
// Close waiting message
if ( total === actual ) {
Ext.Msg.hide();
if ( Ext.isFunction(callback) ) callback();
}
}).catch(err => { console.log(err) })
}).catch(err => { console.log(err) })
}).catch(err => { console.log(err) })
}
// Remove record from localStorage
Ext.getStore('Services').remove(rec);
// Close tab
tab.close();
}
,removeService: function( gridView, rowIndex, colIndex, col, e, rec, rowEl ) {
var me = this;
Ext.Msg.confirm(locale['app.window[12]'], locale['app.window[13]']+' <b>'+rec.get('name')+'</b>?', function(btnId) {
if ( btnId === 'yes' ) {
Ext.Msg.wait('Please wait until we clear all.', 'Removing...');
me.removeServiceFn(rec.get('id'), 1, 1);
}
if ( btnId === 'yes' ) me.removeServiceFn(rec.get('id'));
});
}
,removeAllServices: function(btn, callback) {
var me = this;
// Clear counter for unread messaging
document.title = 'Rambox';
if ( btn ) {
Ext.Msg.confirm(locale['app.window[12]'], locale['app.window[14]'], function(btnId) {
if ( btnId === 'yes' ) {
// Clear counter for unread messaging
document.title = 'Rambox';
Ext.cq1('app-main').suspendEvent('remove');
Ext.getStore('Services').load();
Ext.Msg.wait('Please wait until we clear all.', 'Removing...');
const count = Ext.getStore('Services').getCount();
var i = 1;
Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) {
me.removeServiceFn(serviceId, count, i++, callback || false);
me.removeServiceFn(serviceId);
});
if ( count === 0 && Ext.isFunction(callback) ) callback();
if ( Ext.isFunction(callback) ) callback();
Ext.cq1('app-main').resumeEvent('remove');
document.title = 'Rambox';
}
});
} else {
Ext.cq1('app-main').suspendEvent('remove');
Ext.getStore('Services').load();
const count = Ext.getStore('Services').getCount();
var i = 1;
Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) {
me.removeServiceFn(serviceId, count, i++, callback || false);
me.removeServiceFn(serviceId);
});
if ( count === 0 && Ext.isFunction(callback) ) callback();
if ( Ext.isFunction(callback) ) callback();
Ext.cq1('app-main').resumeEvent('remove');
document.title = 'Rambox';
}
}
@ -297,12 +229,13 @@ Ext.define('Rambox.view.main.MainController', {
,dontDisturb: function(btn, e, called) {
console.info('Dont Disturb:', btn.pressed ? 'Enabled' : 'Disabled');
// Google Analytics Event
if ( !called ) ga_storage._trackEvent('Usability', 'dontDisturb', ( btn.pressed ? 'on' : 'off' ));
Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) {
// Get Tab
var tab = Ext.getCmp('tab_'+serviceId);
if ( !tab ) return; // Skip disabled services
// Mute sounds
tab.setAudioMuted(btn.pressed ? true : tab.record.get('muted'), true);
@ -316,13 +249,6 @@ Ext.define('Rambox.view.main.MainController', {
btn.setText(locale['app.main[16]']+': ' + ( btn.pressed ? locale['app.window[20]'] : locale['app.window[21]'] ));
// var btn_icon = document.getElementById('disturbBtn-btnIconEl');
// btn_icon.innerHTML = btn.pressed ? "" : "";
btn.pressed ? btn.setGlyph('xf1f7@FontAwesome') : btn.setGlyph('xf0f3@FontAwesome');
Ext.getCmp('mainTabBar').getEl().toggleCls('dontdisturb');
// If this method is called from Lock method, prevent showing toast
if ( !e ) return;
Ext.toast({
@ -375,22 +301,14 @@ Ext.define('Rambox.view.main.MainController', {
}
function setLock(text) {
var ramboxTab = Ext.cq1('#ramboxTab');
// Related to issue #2065. Focusing in an sub frame is a workaround
if (ramboxTab.getWebView) {
ramboxTab.down('component').el.dom.executeJavaScript(`
var iframeFix = document.createElement('iframe');
document.body.appendChild(iframeFix);
iframeFix.focus();
document.body.removeChild(iframeFix);
`);
}
console.info('Lock Rambox:', 'Enabled');
// Save encrypted password in localStorage to show locked when app is reopen
localStorage.setItem('locked', text);
// Google Analytics Event
ga_storage._trackEvent('Usability', 'locked');
me.lookupReference('disturbBtn').setPressed(true);
me.dontDisturb(me.lookupReference('disturbBtn'), false, true);
@ -466,18 +384,56 @@ Ext.define('Rambox.view.main.MainController', {
]
}
]
,listeners: {
render: function(win) {
win.getEl().on('click', function() {
win.down('textfield').focus(100);
});
}
}
}).show();
winLock.down('textfield').focus(1000);
}
,openPreferences: function( btn ) {
var me = this;
Ext.create('Rambox.view.preferences.Preferences').show();
}
,login: function(btn) {
var me = this;
Rambox.ux.Auth0.login();
}
,logout: function(btn) {
var me = this;
var logoutFn = function(callback) {
Ext.Msg.wait(locale['app.window[37]'], locale['app.main[21]']);
// Google Analytics Event
ga_storage._trackEvent('Users', 'loggedOut');
// Logout from Auth0
Rambox.ux.Auth0.logout();
Ext.cq1('app-main').getViewModel().set('username', '');
Ext.cq1('app-main').getViewModel().set('avatar', '');
if ( Ext.isFunction(callback) ) callback();
Ext.Msg.hide();
}
if ( btn ) {
Ext.Msg.confirm(locale['app.main[21]'], locale['app.window[38]'], function(btnId) {
if ( btnId === 'yes' ) {
logoutFn(function() {
me.removeAllServices();
});
}
});
} else {
logoutFn();
}
}
,showDonate: function( btn ) {
Tooltip.API.show('zxzKWZfcmgRtHXgth');
}
});

1
app/view/main/MainModel.js

@ -9,6 +9,5 @@ Ext.define('Rambox.view.main.MainModel', {
,username: localStorage.getItem('profile') ? JSON.parse(localStorage.getItem('profile')).name : ''
,avatar: localStorage.getItem('profile') ? JSON.parse(localStorage.getItem('profile')).picture : ''
,last_sync: localStorage.getItem('profile') && JSON.parse(localStorage.getItem('profile')).user_metadata && JSON.parse(localStorage.getItem('profile')).user_metadata.services_lastupdate ? new Date(JSON.parse(localStorage.getItem('profile')).user_metadata.services_lastupdate).toUTCString() : ''
,emptyServices: true
}
});

182
app/view/preferences/Preferences.js

@ -16,15 +16,12 @@ Ext.define('Rambox.view.preferences.Preferences',{
,title: locale['preferences[0]']
,width: 420
,height: 500
,modal: true
,closable: true
,minimizable: false
,maximizable: false
,draggable: true
,resizable: false
,scrollable: 'vertical'
,bodyStyle: 'margin-right:15px;'
,buttons: [
{
text: locale['button[1]']
@ -41,16 +38,6 @@ Ext.define('Rambox.view.preferences.Preferences',{
,initComponent: function() {
var config = ipc.sendSync('getConfig');
var defaultServiceOptions = [];
defaultServiceOptions.push({ value: 'ramboxTab', label: 'Rambox Tab' });
defaultServiceOptions.push({ value: 'last', label: 'Last Active Service' });
Ext.getStore('Services').each(function(rec) {
defaultServiceOptions.push({
value: rec.get('id')
,label: rec.get('name')
});
});
this.items = [
{
xtype: 'form'
@ -74,50 +61,23 @@ Ext.define('Rambox.view.preferences.Preferences',{
,store: Ext.create('Ext.data.Store', {
fields: ['value', 'label']
,data: [
{ 'value': 'af', 'extjs': 'af', 'label': 'Afrikaans' }
,{ 'value': 'ar', 'extjs': 'en', 'label': 'Arabic' }
,{ 'value': 'bs2', 'extjs': 'en', 'label': 'Barndutsch, Switzerland' }
,{ 'value': 'bn', 'extjs': 'en', 'label': 'Bengali' }
,{ 'value': 'bg', 'extjs': 'en', 'label': 'Bulgarian' }
,{ 'value': 'ca', 'extjs': 'ca', 'label': 'Catalan' }
,{ 'value': 'ceb', 'extjs': 'en', 'label': 'Cebuano' }
,{ 'value': 'zh-CN', 'extjs': 'zh', 'label': 'Chinese Simplified' }
,{ 'value': 'zh-TW', 'extjs': 'zh-tw', 'label': 'Chinese Traditional' }
,{ 'value': 'hr', 'extjs': 'en', 'label': 'Croatian' }
,{ 'value': 'cs', 'extjs': 'cs', 'label': 'Czech' }
,{ 'value': 'da', 'extjs': 'da', 'label': 'Danish' }
,{ 'value': 'nl', 'extjs': 'nl', 'label': 'Dutch' }
,{ 'value': 'en', 'extjs': 'en', 'label': 'English' }
,{ 'value': 'fi', 'extjs': 'fi', 'label': 'Finnish' }
,{ 'value': 'fil', 'extjs': 'en', 'label': 'Filipino' }
,{ 'value': 'fr', 'extjs': 'fr', 'label': 'French' }
,{ 'value': 'de', 'extjs': 'de', 'label': 'German' }
,{ 'value': 'de-CH', 'extjs': 'de', 'label': 'German, Switzerland' }
,{ 'value': 'el', 'extjs': 'el', 'label': 'Greek' }
,{ 'value': 'he', 'extjs': 'en', 'label': 'Hebrew' }
,{ 'value': 'hi', 'extjs': 'en', 'label': 'Hindi' }
,{ 'value': 'hu', 'extjs': 'hu', 'label': 'Hungarian' }
,{ 'value': 'id', 'extjs': 'en', 'label': 'Indonesian' }
,{ 'value': 'it', 'extjs': 'it', 'label': 'Italian' }
,{ 'value': 'ja', 'extjs': 'ja', 'label': 'Japanese' }
,{ 'value': 'ko', 'extjs': 'ko', 'label': 'Korean' }
,{ 'value': 'no', 'extjs': 'no', 'label': 'Norwegian' }
,{ 'value': 'fa', 'extjs': 'fa', 'label': 'Persian' }
,{ 'value': 'pl', 'extjs': 'pl', 'label': 'Polish' }
,{ 'value': 'pt-PT', 'extjs': 'pt-br', 'label': 'Portuguese' }
,{ 'value': 'pt-BR', 'extjs': 'pt-br', 'label': 'Portuguese (Brazilian)' }
,{ 'value': 'ro', 'extjs': 'ro', 'label': 'Romanian' }
,{ 'value': 'ru', 'extjs': 'ru', 'label': 'Russian' }
,{ 'value': 'sr', 'extjs': 'en', 'label': 'Serbian (Cyrillic)' }
,{ 'value': 'sk', 'extjs': 'sk', 'label': 'Slovak' }
,{ 'value': 'es-ES', 'extjs': 'es', 'label': 'Spanish' }
,{ 'value': 'sv-SE', 'extjs': 'sv', 'label': 'Swedish' }
,{ 'value': 'tl', 'extjs': 'en', 'label': 'Tagalog' }
,{ 'value': 'th', 'extjs': 'en', 'label': 'Thai' }
,{ 'value': 'tr', 'extjs': 'tr', 'label': 'Turkish' }
,{ 'value': 'uk', 'extjs': 'en', 'label': 'Ukrainian' }
,{ 'value': 'ur-PK', 'extjs': 'en', 'label': 'Urdu (Pakistan)' }
,{ 'value': 'vi', 'extjs': 'en', 'label': 'Vietnamese' }
{ 'value': 'ar', 'auth0': 'en', 'label': 'Arabic' }
,{ 'value': 'cs', 'auth0': 'cs', 'label': 'Czech' }
,{ 'value': 'nl', 'auth0': 'nl', 'label': 'Dutch' }
,{ 'value': 'en', 'auth0': 'en', 'label': 'English' }
,{ 'value': 'fr', 'auth0': 'fr', 'label': 'French' }
,{ 'value': 'de', 'auth0': 'de', 'label': 'German' }
,{ 'value': 'el', 'auth0': 'en', 'label': 'Greek' }
,{ 'value': 'id', 'auth0': 'en', 'label': 'Indonesian' }
,{ 'value': 'it', 'auth0': 'it', 'label': 'Italian' }
,{ 'value': 'ko', 'auth0': 'en', 'label': 'Korean' }
,{ 'value': 'fa', 'auth0': 'fa', 'label': 'Persian' }
,{ 'value': 'pl', 'auth0': 'pl', 'label': 'Polish' }
,{ 'value': 'pt-PT', 'auth0': 'pt-br', 'label': 'Portuguese' }
,{ 'value': 'pt-BR', 'auth0': 'pt-br', 'label': 'Portuguese (Brazilian)' }
,{ 'value': 'ru', 'auth0': 'ru', 'label': 'Russian' }
,{ 'value': 'es-ES', 'auth0': 'es', 'label': 'Spanish' }
,{ 'value': 'tr', 'auth0': 'tr', 'label': 'Turkish' }
]
})
}
@ -152,55 +112,12 @@ Ext.define('Rambox.view.preferences.Preferences',{
,name: 'hide_menu_bar'
,boxLabel: locale['preferences[1]']+' (<code>Alt</code> key to display)'
,value: config.hide_menu_bar
,hidden: process.platform === 'darwin'
}
,{
xtype: 'combo'
,name: 'tabbar_location'
,fieldLabel: locale['preferences[11]']
,labelAlign: 'left'
,width: 380
,labelWidth: 180
,value: config.tabbar_location
,displayField: 'label'
,valueField: 'value'
,editable: false
,store: Ext.create('Ext.data.Store', {
fields: ['value', 'label']
,data: [
{ 'value': 'top', 'label': 'Top' }
,{ 'value': 'left', 'label': 'Left' }
,{ 'value': 'bottom', 'label': 'Bottom' }
,{ 'value': 'right', 'label': 'Right' }
]
})
}
,{
xtype: 'checkbox'
,name: 'hide_tabbar_labels'
,boxLabel: locale['preferences[28]']
,value: config.hide_tabbar_labels
}
,{
xtype: 'combo'
,name: 'default_service'
,fieldLabel: locale['preferences[12]']
,labelAlign: 'top'
//,width: 380
//,labelWidth: 105
,value: config.default_service
,displayField: 'label'
,valueField: 'value'
,editable: false
,store: Ext.create('Ext.data.Store', {
fields: ['value', 'label']
,data: defaultServiceOptions
})
,hidden: process.platform !== 'win32'
}
,{
xtype: 'combo'
,name: 'window_display_behavior'
,fieldLabel: locale['preferences[13]']
,fieldLabel: 'Display behaviour'
,labelAlign: 'left'
,width: 380
,labelWidth: 105
@ -211,9 +128,9 @@ Ext.define('Rambox.view.preferences.Preferences',{
,store: Ext.create('Ext.data.Store', {
fields: ['value', 'label']
,data: [
{ 'value': 'show_taskbar', 'label': locale['preferences[14]'] }
,{ 'value': 'show_trayIcon', 'label': locale['preferences[15]'] }
,{ 'value': 'taskbar_tray', 'label': locale['preferences[16]'] }
{ 'value': 'show_taskbar', 'label': 'Show in Taskbar' }
,{ 'value': 'show_trayIcon', 'label': 'Show Tray Icon' }
,{ 'value': 'taskbar_tray', 'label': 'Show in Taskbar and Tray Icon' }
]
})
,hidden: process.platform === 'darwin'
@ -221,7 +138,7 @@ Ext.define('Rambox.view.preferences.Preferences',{
,{
xtype: 'combo'
,name: 'window_close_behavior'
,fieldLabel: locale['preferences[17]']
,fieldLabel: 'When closing the main window'
,labelAlign: 'left'
,width: 380
,labelWidth: 180
@ -232,9 +149,9 @@ Ext.define('Rambox.view.preferences.Preferences',{
,store: Ext.create('Ext.data.Store', {
fields: ['value', 'label']
,data: [
{ 'value': 'keep_in_tray', 'label': locale['preferences[18]'] }
,{ 'value': 'keep_in_tray_and_taskbar', 'label': locale['preferences[19]'] }
,{ 'value': 'quit', 'label': locale['preferences[20]'] }
{ 'value': 'keep_in_tray', 'label': 'Keep in tray' }
,{ 'value': 'keep_in_tray_and_taskbar', 'label': 'Keep in tray and taskbar' }
,{ 'value': 'quit', 'label': 'Quit' }
]
})
,hidden: process.platform === 'darwin'
@ -242,13 +159,13 @@ Ext.define('Rambox.view.preferences.Preferences',{
,{
xtype: 'checkbox'
,name: 'always_on_top'
,boxLabel: locale['preferences[21]']
,boxLabel: 'Always on top'
,value: config.always_on_top
}
,{
xtype: 'checkbox'
,name: 'systemtray_indicator'
,boxLabel: locale['preferences[22]']
,boxLabel: 'Show System Tray indicator on unread messages'
,value: config.systemtray_indicator
,hidden: process.platform === 'darwin'
}
@ -261,7 +178,7 @@ Ext.define('Rambox.view.preferences.Preferences',{
,{
xtype: 'checkbox'
,name: 'disable_gpu'
,boxLabel: locale['preferences[23]']
,boxLabel: 'Disable Hardware Acceleration (needs to relaunch)'
,value: config.disable_gpu
}
,{
@ -269,20 +186,10 @@ Ext.define('Rambox.view.preferences.Preferences',{
,name: 'enable_hidpi_support'
,boxLabel: locale['preferences[8]']
,value: config.enable_hidpi_support
,hidden: process.platform !== 'win32'
},
{
xtype: 'textfield'
,fieldLabel: 'Override User-Agent for all services (needs to relaunch)'
,labelAlign: 'top'
,name: 'user_agent'
,value: config.user_agent
,width: 360
,emptyText: 'Leave blank for default user agent'
}
,{
xtype: 'fieldset'
,title: locale['preferences[24]']
,title: 'Master Password - Ask for password on startup'
,collapsed: !config.master_password
,checkboxToggle: true
,checkboxName: 'master_password'
@ -294,7 +201,7 @@ Ext.define('Rambox.view.preferences.Preferences',{
{
xtype: 'textfield'
,inputType: 'password'
,fieldLabel: locale['preferences[25]']
,fieldLabel: 'Password'
,name: 'master_password1'
,itemId: 'pass'
,flex: 1
@ -310,7 +217,7 @@ Ext.define('Rambox.view.preferences.Preferences',{
,{
xtype: 'textfield'
,inputType: 'password'
,fieldLabel: locale['preferences[26]']
,fieldLabel: 'Repeat Password'
,name: 'master_password2'
,margin: '0 0 0 10'
,vtype: 'password'
@ -321,14 +228,14 @@ Ext.define('Rambox.view.preferences.Preferences',{
}
,{
xtype: 'fieldset'
,title: 'Proxy (needs to relaunch) - <a href="https://github.com/saenzramiro/rambox/wiki/FREE-PROXY-SERVERS" target="_blank">Free Proxy Servers</a>'
,title: 'Proxy (needs to relaunch) - <a href="http://proxylist.hidemyass.com/" target="_blank">Free IP:PORT Proxy List</a>'
,collapsed: !config.proxy
,checkboxToggle: true
,checkboxName: 'proxy'
,margin: '10 0 0 0'
,padding: 10
,layout: 'vbox'
,defaults: { labelAlign: 'left' }
,layout: 'hbox'
,defaults: { labelAlign: 'top' }
,items: [
{
xtype: 'textfield'
@ -336,28 +243,15 @@ Ext.define('Rambox.view.preferences.Preferences',{
,fieldLabel: 'Host'
,name: 'proxyHost'
,value: config.proxyHost
//,flex: 1
,flex: 1
}
,{
xtype: 'numberfield'
,fieldLabel: 'Port'
,name: 'proxyPort'
,width: 100
,value: config.proxyPort
}
,{
xtype: 'textfield'
,fieldLabel: 'Login'
,name: 'proxyLogin'
,value: config.proxyLogin
,emptyText: 'Optional'
}
,{
xtype: 'textfield'
,fieldLabel: 'Password'
,name: 'proxyPassword'
,value: config.proxyPassword
,emptyText: 'Optional'
,inputType: 'password'
,margin: '0 0 0 10'
}
]
}

15
app/view/preferences/PreferencesController.js

@ -39,23 +39,10 @@ Ext.define('Rambox.view.preferences.PreferencesController', {
// Proxy
if ( values.proxy && (Ext.isEmpty(values.proxyHost) || Ext.isEmpty(values.proxyPort)) ) return;
// Display behaviour
if ( values.window_display_behavior === 'show_taskbar' && values.window_close_behavior === 'keep_in_tray' ) {
Ext.Msg.alert('Action required', 'You need to change the window closing behaviour because "Keep in tray" is not possible.');
return;
}
// User Agent
if ( values.user_agent !== ipc.sendSync('getConfig').user_agent ) {
Ext.Msg.confirm('Action required', 'To change the user agent of Rambox, you need to reload the app. Do you want to do it now?', function(btnId) {
if ( btnId === 'yes' ) ipc.send('relaunchApp');
});
}
// Locale
if ( values.locale !== ipc.sendSync('getConfig').locale ) {
localStorage.setItem('locale', values.locale);
localStorage.setItem('locale-extjs', me.getView().down('form').down('combo[name="locale"]').getSelection().get('extjs'));
localStorage.setItem('locale-auth0', me.getView().down('form').down('combo[name="locale"]').getSelection().get('auth0'));
Ext.Msg.confirm('Action required', 'To change the language of Rambox, you need to reload the app. Do you want to do it now?', function(btnId) {
if ( btnId === 'yes' ) ipc.send('relaunchApp');
});

52
appveyor.yml

@ -1,6 +1,4 @@
environment:
matrix:
- nodejs_version: '14.16.1'
version: 0.5.11
pull_requests:
do_not_increment_build_number: true
branches:
@ -8,18 +6,46 @@ branches:
- master
skip_non_tags: true
init:
- cmd: git config --global core.autocrlf input
- cmd: node --version
- cmd: npm --version
- cmd: git config --global core.autocrlf input
install:
- ps: Update-NodeJsInstallation $env:nodejs_version x64
- cmd: >-
git clone https://github.com/saenzramiro/rambox-build.git %APPVEYOR_BUILD_FOLDER%\build\production\Rambox\
- cmd: >-
git reset --hard HEAD
cd %APPVEYOR_BUILD_FOLDER%\build\production\Rambox\
npm run sencha:clean
npm install
git clone https://github.com/saenzramiro/rambox-build.git %APPVEYOR_BUILD_FOLDER%\build\production\Rambox\
npm install npm -g
npm install
npm --prefix %APPVEYOR_BUILD_FOLDER%\build\production\Rambox\ install %APPVEYOR_BUILD_FOLDER%\build\production\Rambox\
npm prune
cache:
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- node_modules
- build\production\Rambox\node_modules
build_script:
- cmd: cd %APPVEYOR_BUILD_FOLDER%\build\production\Rambox\
- cmd: npm run build:win
- cmd: >-
node --version
npm --version
node_modules/.bin/build --win --ia32 --x64
test: off
artifacts:
- path: dist\win\*.exe
- path: dist\win\*.nupkg
- path: dist\win\RELEASES
- path: dist\win-ia32\*.exe
- path: dist\*.zip
deploy:
- provider: GitHub
tag: $(appveyor_build_version)
release: $(appveyor_build_version)
auth_token:
secure: RKFkz2S89dkYXdW+k47Wv42yrUeE1bDDMkM1PNuxrb0ZOcVVXPO88+t5dUllyvOG
draft: true
prerelease: false

389
electron/main.js

@ -1,12 +1,12 @@
'use strict';
const {app, BrowserWindow, shell, Menu, ipcMain, nativeImage, session} = require('electron');
const {app, protocol, BrowserWindow, dialog, shell, Menu, ipcMain, nativeImage, session} = require('electron');
// Tray
const tray = require('./tray');
// AutoLaunch
var AutoLaunch = require('auto-launch-patched');
// Configuration
const Config = require('electron-store');
const Config = require('electron-config');
// Development
const isDev = require('electron-is-dev');
// Updater
@ -15,18 +15,11 @@ const updater = require('./updater');
var fs = require("fs");
const path = require('path');
// Disk usage:
// const disk = require('diskusage');
if ( isDev ) app.getVersion = function() { return require('../package.json').version; }; // FOR DEV ONLY, BECAUSE IN DEV RETURNS ELECTRON'S VERSION
// Initial Config
const config = new Config({
defaults: {
always_on_top: false
,hide_menu_bar: false
,tabbar_location: 'top'
,hide_tabbar_labels: false
,window_display_behavior: 'taskbar_tray'
,auto_launch: !isDev
,flash_frame: true
@ -39,13 +32,8 @@ const config = new Config({
,proxy: false
,proxyHost: ''
,proxyPort: ''
,proxyLogin: ''
,proxyPassword: ''
,locale: 'en'
,enable_hidpi_support: false
,user_agent: ''
,default_service: 'ramboxTab'
,sendStatistics: false
,x: undefined
,y: undefined
@ -61,29 +49,89 @@ if (config.get('enable_hidpi_support') && (process.platform === 'win32')) {
app.commandLine.appendSwitch('force-device-scale-factor', '1')
}
app.commandLine.appendSwitch('lang', config.get('locale') === 'en' ? 'en-US' : config.get('locale'));
// Temporary fix to load Twitter and other websites inside webviews
// Bug related with Electron: https://github.com/electron/electron/issues/25469
app.commandLine.appendSwitch('disable-features', 'CrossOriginOpenerPolicy');
// Because we build it using Squirrel, it will assign UserModelId automatically, so we match it here to display notifications correctly.
// https://github.com/electron-userland/electron-builder/issues/362
app.setAppUserModelId('com.grupovrs.ramboxce');
app.setAppUserModelId('com.squirrel.Rambox.Rambox');
// Menu
const appMenu = require('./menu')(config);
// Configure AutoLaunch
let appLauncher;
if ( !isDev ) {
appLauncher = new AutoLaunch({
name: 'Rambox'
,isHidden: config.get('start_minimized')
});
config.get('auto_launch') ? appLauncher.enable() : appLauncher.disable();
const appLauncher = new AutoLaunch({
name: 'Rambox'
,isHidden: config.get('start_minimized')
});
config.get('auto_launch') && !isDev ? appLauncher.enable() : appLauncher.disable();
// this should be placed at top of main.js to handle setup events quickly
if (handleSquirrelEvent()) {
// squirrel event handled and app will exit in 1000ms, so don't do anything else
return;
}
function handleSquirrelEvent() {
if (process.argv.length === 1) {
return false;
}
const ChildProcess = require('child_process');
const appFolder = path.resolve(process.execPath, '..');
const rootAtomFolder = path.resolve(appFolder, '..');
const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
const exeName = path.basename(process.execPath);
const spawn = function(command, args) {
let spawnedProcess, error;
try {
spawnedProcess = ChildProcess.spawn(command, args, {detached: true});
} catch (error) {}
return spawnedProcess;
};
const spawnUpdate = function(args) {
return spawn(updateDotExe, args);
};
const squirrelEvent = process.argv[1];
switch (squirrelEvent) {
case '--squirrel-install':
case '--squirrel-updated':
// Optionally do things such as:
// - Add your .exe to the PATH
// - Write to the registry for things like file associations and
// explorer context menus
// Install desktop and start menu shortcuts
spawnUpdate(['--createShortcut', exeName]);
setTimeout(app.quit, 1000);
return true;
case '--squirrel-uninstall':
// Undo anything you did in the --squirrel-install and
// --squirrel-updated handlers
// Remove desktop and start menu shortcuts
spawnUpdate(['--removeShortcut', exeName]);
// Remove user app data
require('rimraf').sync(require('electron').app.getPath('userData'));
setTimeout(app.quit, 1000);
return true;
case '--squirrel-obsolete':
// This is called on the outgoing version of your app before
// we update to the new version - it's the opposite of
// --squirrel-updated
app.quit();
return true;
}
};
// 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.
let mainWindow;
@ -93,7 +141,7 @@ function createWindow () {
// Create the browser window using the state information
mainWindow = new BrowserWindow({
title: 'Rambox'
,icon: __dirname + '/../resources/Icon.' + (process.platform === 'linux' ? 'png' : 'ico')
,icon: __dirname + '/../resources/Icon.ico'
,backgroundColor: '#FFF'
,x: config.get('x')
,y: config.get('y')
@ -105,31 +153,14 @@ function createWindow () {
,show: !config.get('start_minimized')
,acceptFirstMouse: true
,webPreferences: {
enableRemoteModule: true
webSecurity: false
,nodeIntegration: true
,plugins: true
,partition: 'persist:rambox'
,nodeIntegration: true
,webviewTag: true
,contextIsolation: false
,spellcheck: false
}
});
// Check if user has defined a custom User-Agent
if ( config.get('user_agent').length > 0 ) mainWindow.webContents.setUserAgent( config.get('user_agent') );
// Wait for the mainWindow.loadURL(..) and the optional mainWindow.webContents.openDevTools()
// to be finished before minimizing
config.get('start_minimized') && mainWindow.webContents.once('did-finish-load', () => config.get('window_display_behavior') === 'show_trayIcon' ? mainWindow.hide() : mainWindow.minimize());
// Check if the window its outside of the view (ex: multi monitor setup)
const { positionOnScreen } = require('./utils/positionOnScreen');
const inBounds = positionOnScreen([config.get('x'), config.get('y')]);
if ( inBounds ) {
mainWindow.setPosition(config.get('x'), config.get('y'));
} else {
mainWindow.center();
}
if ( !config.get('start_minimized') && config.get('maximized') ) mainWindow.maximize();
process.setMaxListeners(10000);
@ -143,7 +174,7 @@ function createWindow () {
tray.create(mainWindow, config);
updater.initialize(mainWindow);
if ( fs.existsSync(path.resolve(path.dirname(process.execPath), '..', 'Update.exe')) && process.argv.indexOf('--without-update') === -1 ) updater.initialize(mainWindow);
// Open links in default browser
mainWindow.webContents.on('new-window', function(e, url, frameName, disposition, options) {
@ -152,7 +183,6 @@ function createWindow () {
case 'new-window':
e.preventDefault();
const win = new BrowserWindow(options);
if ( config.get('user_agent').length > 0 ) win.webContents.setUserAgent( config.get('user_agent') );
win.once('ready-to-show', () => win.show());
win.loadURL(url);
e.newGuest = win;
@ -212,15 +242,6 @@ function createWindow () {
}
}
});
mainWindow.on('minimize', function(e) {
if ( config.get('window_display_behavior') === 'show_trayIcon' ) mainWindow.setSkipTaskbar(true);
});
mainWindow.on('restore', function(e) {
if ( config.get('window_display_behavior') === 'show_taskbar' ) mainWindow.setSkipTaskbar(false);
});
mainWindow.on('show', function(e) {
if ( config.get('window_display_behavior') !== 'show_trayIcon' ) mainWindow.setSkipTaskbar(false);
});
mainWindow.on('closed', function(e) {
mainWindow = null;
});
@ -232,10 +253,6 @@ function createMasterPasswordWindow() {
mainMasterPasswordWindow = new BrowserWindow({
backgroundColor: '#0675A0'
,frame: false
,webPreferences: {
nodeIntegration: true
,enableRemoteModule: true
}
});
// Open the DevTools.
if ( isDev ) mainMasterPasswordWindow.webContents.openDevTools();
@ -245,14 +262,16 @@ function createMasterPasswordWindow() {
}
function updateBadge(title) {
title = title.split(" - ")[0]; //Discard service name if present, could also contain digits
var messageCount = title.match(/\d+/g) ? parseInt(title.match(/\d+/g).join("")) : 0;
messageCount = isNaN(messageCount) ? 0 : messageCount;
tray.setBadge(messageCount, config.get('systemtray_indicator'));
if (process.platform === 'win32') {
if (messageCount === 0) return mainWindow.setOverlayIcon(null, '');
if (process.platform === 'win32') { // Windows
if (messageCount === 0) {
mainWindow.setOverlayIcon(null, "");
return;
}
mainWindow.webContents.send('setBadge', messageCount);
} else { // macOS & Linux
app.setBadgeCount(messageCount);
@ -261,51 +280,15 @@ function updateBadge(title) {
if ( messageCount > 0 && !mainWindow.isFocused() && !config.get('dont_disturb') && config.get('flash_frame') ) mainWindow.flashFrame(true);
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
/* async function availableSpaceWatchDog() {
// optionally render this information also in rambox window
try {
const { available } = await disk.check(appPath);
if (available < 1073741824) { // 1 GB
const options = {
type: 'warning',
buttons: ['OK, quit'],
defaultId: 0,
title: `Running out of disk space! - Rambox shutting down`,
detail: `You've got just ${formatBytes(available)} space left.\n\nRambox has been frozen to prevent settings corruption.\n\nOnce you quit this dialog, Rambox will shutdown.\n\n1 GB of avalable disk space is required.\nFree up space on partition where Rambox is installed then start the app again.\n\nRambox path: \n${appPath}`,
message: `Running out of disk space! - Rambox shutting down`,
};
dialog.showMessageBoxSync(null, options);
app.quit();
}
} catch (err) {
console.error(err)
}
} */
ipcMain.on('setBadge', function(event, messageCount, value) {
mainWindow.setOverlayIcon(nativeImage.createFromDataURL(value), messageCount.toString());
var img = nativeImage.createFromDataURL(value);
mainWindow.setOverlayIcon(img, messageCount.toString());
});
ipcMain.on('getConfig', function(event, arg) {
event.returnValue = config.store;
});
ipcMain.on('sConfig', function(event, values) {
config.set(values);
event.returnValue = config;
});
ipcMain.on('setConfig', function(event, values) {
config.set(values);
@ -315,12 +298,10 @@ ipcMain.on('setConfig', function(event, values) {
// always_on_top
mainWindow.setAlwaysOnTop(values.always_on_top);
// auto_launch
if ( !isDev ) values.auto_launch ? appLauncher.enable() : appLauncher.disable();
values.auto_launch ? appLauncher.enable() : appLauncher.disable();
// systemtray_indicator
updateBadge(mainWindow.getTitle());
mainWindow.webContents.executeJavaScript('(function(a){if(a)a.controller.initialize(a)})(Ext.cq1("app-main"))');
switch ( values.window_display_behavior ) {
case 'show_taskbar':
mainWindow.setSkipTaskbar(false);
@ -339,10 +320,6 @@ ipcMain.on('setConfig', function(event, values) {
}
});
ipcMain.on('sendStatistics', function(event) {
event.returnValue = config.get('sendStatistics');
});
ipcMain.on('validateMasterPassword', function(event, pass) {
if ( config.get('master_password') === require('crypto').createHash('md5').update(pass).digest('hex') ) {
createWindow();
@ -354,7 +331,6 @@ ipcMain.on('validateMasterPassword', function(event, pass) {
// Handle Service Notifications
ipcMain.on('setServiceNotifications', function(event, partition, op) {
if ( partition === null ) return;
session.fromPartition(partition).setPermissionRequestHandler(function(webContents, permission, callback) {
if (permission === 'notifications') return callback(op);
callback(true)
@ -363,7 +339,7 @@ ipcMain.on('setServiceNotifications', function(event, partition, op) {
ipcMain.on('setDontDisturb', function(event, arg) {
config.set('dont_disturb', arg);
});
})
// Reload app
ipcMain.on('reloadApp', function(event) {
@ -376,12 +352,7 @@ ipcMain.on('relaunchApp', function(event) {
app.exit(0);
});
const shouldQuit = app.requestSingleInstanceLock();
if (!shouldQuit) {
app.quit();
return;
}
app.on('second-instance', (event, commandLine, workingDirectory) => {
const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
@ -392,85 +363,10 @@ app.on('second-instance', (event, commandLine, workingDirectory) => {
}
});
// ALLOWED URLS POPUPS
let allowPopUp = [
'feedly.com/v3/auth/',
'identity.linuxfoundation.org/cas/login',
'auth.missiveapp.com',
'accounts.google.com/AccountChooser',
'facebook.com/v3.1/dialog/oauth?',
'accounts.google.com/o/oauth2',
'app.slack.com/files/import/gdrive',
'spikenow.com/s/account',
'app.mixmax.com/_oauth/google',
'officeapps.live.com',
'dropbox.com/profile_services/start_auth_flow',
'facebook.com/v3.2/dialog/oauth?',
'notion.so/googlepopupredirect',
'zoom.us/office365',
'figma.com/start_google_sso',
'mail.google.com/mail',
'app.slack.com/free-willy/',
'messenger.com/videocall',
'api.moo.do',
'manychat.com/fb?popup',
'=?print=true' // esta ultima checkea como anda imprimir un pedf desde gmail, si no va bie sacala
];
app.on('web-contents-created', (webContentsCreatedEvent, contents) => {
if (contents.getType() !== 'webview') return;
// Block some Deep links to prevent that open its app (Ex: Slack)
contents.on('will-navigate', (event, url) => url.substring(0, 8) === 'slack://' && event.preventDefault());
// New Window handler
contents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures, referrer, postBody) => {
// If the url is about:blank we allow the window and handle it in 'did-create-window'
if (['about:blank', 'about:blank#blocked'].includes(url)) {
event.preventDefault();
Object.assign(options, { show: false });
const win = new BrowserWindow(options);
win.center();
let once = false;
win.webContents.on('will-navigate', (e, nextURL) => {
if (once) return;
if (['about:blank', 'about:blank#blocked'].includes(nextURL)) return;
once = true;
let allow = false;
allowPopUp.forEach(url => nextURL.indexOf(url) > -1 && (allow = true));
// If the url is in aboutBlankOnlyWindow we handle this as a popup window
if (allow) return win.show();
shell.openExternal(nextURL);
win.close()
})
event.newGuest = win;
return;
}
// We check if url is in the allowPopUpLoginURLs or allowForegroundTabURLs in Firebase to open a as a popup,
// if it is not we send this to the app
let allow = false;
allowPopUp.forEach(allowed => url.indexOf(allowed) > -1 && (allow = true));
if (allow) return;
shell.openExternal(url);
event.preventDefault();
});
contents.on('did-create-window', (win, details) => {
// Here we center the new window.
win.center();
// The following code is for handling the about:blank cases only.
if (!['about:blank', 'about:blank#blocked'].includes(details.url)) return;
let once = false;
win.webContents.on('will-navigate', (e, nextURL) => {
if (once) return;
if (['about:blank', 'about:blank#blocked'].includes(nextURL)) return;
once = true;
let allow = false;
allowPopUp.forEach(url => nextURL.indexOf(url) > -1 && (allow = true));
// If the url is in aboutBlankOnlyWindow we handle this as a popup window
if (allow) return win.show();
shell.openExternal(url);
win.close();
});
});
});
if (shouldQuit) {
app.quit();
return;
}
// Code for downloading images as temporal files
// Credit: Ghetto Skype (https://github.com/stanfieldr/ghetto-skype)
@ -495,8 +391,6 @@ ipcMain.on('image:download', function(event, url, partition) {
}
});
if ( config.get('user_agent').length > 0 ) tmpWindow.webContents.setUserAgent( config.get('user_agent') );
tmpWindow.webContents.session.once('will-download', (event, downloadItem) => {
imageCache[url] = file = {
path: tmp.tmpNameSync() + '.' + mime.extension(downloadItem.getMimeType())
@ -530,110 +424,33 @@ ipcMain.on('image:popup', function(event, url, partition) {
}
});
if ( config.get('user_agent').length > 0 ) tmpWindow.webContents.setUserAgent( config.get('user_agent') );
tmpWindow.maximize();
tmpWindow.loadURL(url);
});
ipcMain.on('toggleWin', function(event, allwaysShow) {
if ( config.get('window_display_behavior') !== 'show_trayIcon' ) mainWindow.setSkipTaskbar(false);
if ( !mainWindow.isMinimized() && mainWindow.isMaximized() && mainWindow.isVisible() ) { // Maximized
!allwaysShow ? mainWindow.close() : mainWindow.show();
} else if ( mainWindow.isMinimized() && !mainWindow.isMaximized() && !mainWindow.isVisible() ) { // Minimized
if ( process.platform === 'linux' ) {
mainWindow.minimize();
mainWindow.restore();
mainWindow.focus();
return
}
mainWindow.restore();
} else if ( !mainWindow.isMinimized() && !mainWindow.isMaximized() && mainWindow.isVisible() ) { // Windowed mode
!allwaysShow ? mainWindow.close() : mainWindow.show();
} else if ( mainWindow.isMinimized() && !mainWindow.isMaximized() && mainWindow.isVisible() ) { // Closed to taskbar
if ( process.platform === 'linux' ) {
mainWindow.minimize();
mainWindow.restore();
mainWindow.focus();
return
}
mainWindow.restore();
} else if ( !mainWindow.isMinimized() && mainWindow.isMaximized() && !mainWindow.isVisible() ) { // Closed maximized to tray
mainWindow.show();
} else if ( !mainWindow.isMinimized() && !mainWindow.isMaximized() && !mainWindow.isVisible() ) { // Closed windowed to tray
mainWindow.show();
} else if ( mainWindow.isMinimized() && !mainWindow.isMaximized() && !mainWindow.isVisible() ) { // Closed minimized to tray
mainWindow.show();
} else {
if ( process.platform === 'linux' ) {
mainWindow.minimize();
mainWindow.maximize();
mainWindow.focus();
return
}
mainWindow.restore();
} else {
mainWindow.show();
}
});
// ScreenShare
ipcMain.on('screenShare:show', (event, screenList) => {
let tmpWindow = new BrowserWindow({
title: 'Rambox - Select screen',
width: 600,
height: 500,
icon: __dirname + '/../resources/Icon.ico',
autoHideMenuBar: true,
transparent: true,
show: true,
frame: false,
hasShadow: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
const close = () => {
tmpWindow.close();
tmpWindow = null;
};
const onCancel = () => {
event.sender.send('screenShare:cancel');
close();
};
const onShare = (_, shareId) => {
event.sender.send('screenShare:share', shareId);
close();
};
ipcMain.handle('screenShare:getSources', () => screenList);
ipcMain.on('screenShare:cancelSelection', onCancel);
ipcMain.on('screenShare:selectScreen', onShare);
tmpWindow.on('closed', () => {
ipcMain.removeHandler('screenShare:getSources');
ipcMain.removeAllListeners('screenShare:cancelSelection');
ipcMain.removeAllListeners('screenShare:selectScreen');
});
tmpWindow.loadFile(__dirname + '/../screenselector.html');
});
// Proxy
if ( config.get('proxy') ) {
app.commandLine.appendSwitch('proxy-server', config.get('proxyHost')+':'+config.get('proxyPort'));
app.on('login', (event, webContents, request, authInfo, callback) => {
if(!authInfo.isProxy)
return;
event.preventDefault();
callback(config.get('proxyLogin'), config.get('proxyPassword'))
})
}
if ( config.get('proxy') ) app.commandLine.appendSwitch('proxy-server', config.get('proxyHost')+':'+config.get('proxyPort'));
// Disable GPU Acceleration for Linux
// to prevent White Page bug
@ -645,8 +462,8 @@ if ( config.get('disable_gpu') ) app.disableHardwareAcceleration();
// initialization and is ready to create browser windows.
app.on('ready', function() {
config.get('master_password') ? createMasterPasswordWindow() : createWindow();
// setInterval(availableSpaceWatchDog, 1000 * 60);
});
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
@ -663,9 +480,7 @@ app.on('activate', function () {
config.get('master_password') ? createMasterPasswordWindow() : createWindow();
}
if (mainWindow) {
mainWindow.show();
}
if ( mainWindow !== null ) mainWindow.show();
});
app.on('before-quit', function () {

87
electron/menu.js

@ -1,7 +1,6 @@
'use strict';
const os = require('os');
const electron = require('electron');
const { systemPreferences } = require('electron')
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const shell = electron.shell;
@ -23,7 +22,7 @@ module.exports = function(config) {
{
label: `&`+locale['menu.help[0]'],
click() {
shell.openExternal('https://rambox.app');
shell.openExternal('http://rambox.pro');
}
},
{
@ -41,24 +40,44 @@ module.exports = function(config) {
{
label: `&GitHub`,
click() {
shell.openExternal('https://github.com/ramboxapp/community-edition');
shell.openExternal('https://www.github.com/saenzramiro/rambox');
}
},
{
type: 'separator'
},
{
label: '&'+locale['menu.help[1]'],
click() {
const body = `
<!-- Please describe here your issue and steps to reproduce it. -->
<!-- DON'T REMOVE THE FOLLOWING LINES -->
-
> ${app.getName()} ${app.getVersion()}
> Electron ${process.versions.electron}
> ${process.platform} ${process.arch} ${os.release()}`;
shell.openExternal(`https://github.com/saenzramiro/rambox/issues/new?body=${encodeURIComponent(body)}`);
}
},
{
label: `&`+locale['menu.help[2]'],
click() {
shell.openExternal('https://gitter.im/saenzramiro/rambox');
}
},
{
label: `&Tools`,
submenu: [
{
label: `&Clear Cache`,
click(item, win) {
win.webContents.session.clearCache()
.then(() => {
win.webContents.session.clearCache(function() {
win.reload();
}).catch(err => {
console.log(err)
})
});
}
},
{
@ -66,15 +85,22 @@ module.exports = function(config) {
click(item, win) {
win.webContents.session.clearStorageData({
storages: ['localstorage']
}).then(() => {
}, function() {
win.reload();
}).catch(err => {
console.log(err)
})
});
}
}
]
},
{
type: 'separator'
},
{
label: `&`+locale['menu.help[3]'],
click() {
shell.openExternal('https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WU75QWS7LH2CA');
}
}
];
let tpl = [
@ -137,28 +163,13 @@ module.exports = function(config) {
type: 'separator'
},
{
label: '&Toggle Status Bar',
click() {
sendAction('toggleStatusBar');
}
role: 'zoomin'
},
{
type: 'separator'
role: 'zoomout'
},
{
label: 'Zoom In',
accelerator: 'Ctrl+Plus',
click(item, win) { win.webContents.send('zoomin-webview')}
},
{
label: 'Zoom Out',
accelerator: 'Ctrl+-',
click(item, win) { win.webContents.send('zoomout-webview')}
},
{
label: 'Reset Zoom',
accelerator: 'Ctrl+0',
click(item, win) { win.webContents.send('resetzoom-webview')}
role: 'resetzoom'
}
]
},
@ -198,7 +209,7 @@ module.exports = function(config) {
}
];
if ( process.platform === 'darwin' ) {
if (process.platform === 'darwin') {
tpl.unshift({
label: appName,
submenu: [
@ -210,7 +221,6 @@ module.exports = function(config) {
},
{
label: locale['menu.help[5]'],
visible: process.argv.indexOf('--without-update') === -1,
click(item, win) {
const webContents = win.webContents;
const send = webContents.send.bind(win.webContents);
@ -257,18 +267,6 @@ module.exports = function(config) {
}
]
});
helpSubmenu.push({
type: 'separator'
});
helpSubmenu.push({
label: 'Grant Microphone and Camera permissions',
visible: systemPreferences.getMediaAccessStatus('microphone') !== 'granted' || systemPreferences.getMediaAccessStatus('camera') !== 'granted',
click(item, win) {
const webContents = win.webContents;
const send = webContents.send.bind(win.webContents);
send('grantPermissions');
}
});
} else {
tpl.unshift({
label: '&'+locale['menu.file[0]'],
@ -293,7 +291,6 @@ module.exports = function(config) {
});
helpSubmenu.push({
label: `&`+locale['menu.help[5]'],
visible: process.argv.indexOf('--without-update') === -1,
click(item, win) {
const webContents = win.webContents;
const send = webContents.send.bind(win.webContents);

31
electron/tray.js

@ -34,27 +34,9 @@ exports.create = function(win, config) {
appIcon = new Tray(iconPath);
appIcon.setToolTip('Rambox');
appIcon.setContextMenu(contextMenu);
switch (process.platform) {
case 'darwin':
break;
case 'linux':
case 'freebsd':
case 'sunos':
// Double click is not supported and Click its only supported when app indicator is not used.
// Read more here (Platform limitations): https://github.com/electron/electron/blob/master/docs/api/tray.md
appIcon.on('click', function() {
win.webContents.executeJavaScript('ipc.send("toggleWin", false);');
});
break;
case 'win32':
appIcon.on('double-click', function() {
win.webContents.executeJavaScript('ipc.send("toggleWin", false);');
});
break;
default:
break;
}
appIcon.on('double-click', function() {
win.webContents.executeJavaScript('ipc.send("toggleWin", true);');
});
};
exports.destroy = function() {
@ -66,13 +48,12 @@ exports.setBadge = function(messageCount, showUnreadTray) {
if (process.platform === 'darwin' || !appIcon) return;
let icon;
messageCount = parseInt(messageCount);
if (process.platform === 'linux') {
icon = messageCount > 0 && showUnreadTray ? 'IconTrayUnread.png' : 'IconTray.png';
icon = messageCount && showUnreadTray ? 'IconTrayUnread.png' : 'IconTray.png';
} else {
icon = messageCount > 0 && showUnreadTray ? 'IconTrayUnread.ico' : 'Icon.ico';
icon = messageCount && showUnreadTray ? 'IconTrayUnread.ico' : 'Icon.ico';
}
const iconPath = path.join(__dirname, `../resources/${icon}`);
appIcon.setImage(electron.nativeImage.createFromPath(iconPath));
appIcon.setImage(iconPath);
};

32
electron/updater.js

@ -1,29 +1,19 @@
const { app, ipcMain, BrowserWindow } = require('electron');
const { autoUpdater } = require("electron-updater");
// autoUpdater.logger = require("electron-log");
// autoUpdater.logger.transports.file.level = "debug";
// autoUpdater.currentVersion = '0.8.0';
// autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml');
autoUpdater.setFeedURL({
"provider": "github",
"owner": "ramboxapp",
"repo": "download",
"vPrefixedTagName": true
});
const {app, autoUpdater, ipcMain} = require('electron');
const version = app.getVersion();
const platform = process.platform === 'darwin' ? 'osx' : process.platform;
const url = `https://getrambox.herokuapp.com/update/${platform}/${version}`;
const initialize = (window) => {
const webContents = window.webContents;
const send = webContents.send.bind(window.webContents);
autoUpdater.on('checking-for-update', (event) => send('autoUpdater:checking-for-update'));
autoUpdater.on('update-downloaded', (...args) => send('autoUpdater:update-downloaded', ...args));
ipcMain.on('autoUpdater:quit-and-install', (event) => {
app.removeAllListeners('window-all-closed');
BrowserWindow.getAllWindows().forEach((browserWindow) => browserWindow.removeAllListeners('close'));
autoUpdater.quitAndInstall(true, true);
});
autoUpdater.on('checking-for-update', (event) => send('autoUpdater:checking-for-update:'));
autoUpdater.on('update-downloaded', (event, ...args) => send('autoUpdater:update-downloaded', ...args));
ipcMain.on('autoUpdater:quit-and-install', (event) => autoUpdater.quitAndInstall());
ipcMain.on('autoUpdater:check-for-updates', (event) => autoUpdater.checkForUpdates());
webContents.on('did-finish-load', () => {
autoUpdater.setFeedURL(url);
//autoUpdater.checkForUpdates();
});
};
module.exports = {initialize};

18
electron/utils/positionOnScreen.js

@ -1,18 +0,0 @@
const { screen } = require('electron');
const positionOnScreen = (position) => {
let inBounds = false;
if (position) {
screen.getAllDisplays().forEach((display) => {
if (position[0] >= display.workArea.x &&
position[0] <= display.workArea.x + display.workArea.width &&
position[1] >= display.workArea.y &&
position[1] <= display.workArea.y + display.workArea.height) {
inBounds = true;
}
});
}
return inBounds;
};
module.exports = {positionOnScreen};

4
env-sample.js

@ -0,0 +1,4 @@
var auth0Cfg = {
clientID: ''
,domain: ''
};

2
ext/packages/ext-aria/build/resources/Readme.md

@ -1,3 +1,3 @@
# ext-aria/resources
This folder contains static resources (typically an `images` folder as well).
This folder contains static resources (typically an `"images"` folder as well).

2
ext/packages/ext-aria/resources/Readme.md

@ -1,3 +1,3 @@
# ext-aria/resources
This folder contains static resources (typically an `images` folder as well).
This folder contains static resources (typically an `"images"` folder as well).

4
ext/packages/ext-aria/sass/etc/Readme.md

@ -1,4 +1,4 @@
# ext-aria/sass/etc
This folder contains miscellaneous SASS files.
Unlike `ext-aria/sass/etc`, these files need to be used explicitly.
This folder contains miscellaneous SASS files. Unlike `"ext-aria/sass/etc"`, these files
need to be used explicitly.

1
ext/packages/sencha-soap/Readme.md

@ -1 +1,2 @@
# sencha-soap - Read Me

63
index.html

@ -1,6 +1,5 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
@ -8,12 +7,34 @@
<title>Rambox</title>
<script type='text/javascript'>
var xhr = new XMLHttpRequest();
var file = "http://rambox.pro/api/latestversion.json";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, true);
xhr.send();
xhr.addEventListener("readystatechange", processRequest, false);
function processRequest(e) {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 304) {
console.info('Internet Connection', true);
} else {
console.info('Internet Connection', false);
alert('Please, check your internet connection and restart Rambox. If you use a Proxy, please go to Preferences to configure it.', 'No Internet Connection');
}
}
}
</script>
<!-- The line below must be kept intact for Sencha Cmd to build your application -->
<script id="microloader" type="text/javascript" src="bootstrap.js"></script>
<!--- Localization -->
<script type='text/javascript'>
(function(d, s){
(function(d, s){
var t = d.createElement(s), e = d.getElementsByTagName(s)[0];
t.type = "text/javascript"; e.parentNode.insertBefore(t, e);
t.src = "resources/languages/"+require('electron').ipcRenderer.sendSync('getConfig').locale+".js";
@ -21,26 +42,19 @@
</script>
<!--- Localization -->
<!--- Signalayer player -->
<!--- Tooltip player -->
<script type='text/javascript'>
window.Signalayer||function(t,e){var o={
url:"https://cdn.signalayer.com/static/player.js",
key:"sEF8shc4KSuqtHMx0ztmTFdcrQO0cAVpszZ9Y8hLfQ2",
async:true
};window.Signalayer={cs:[],_apiKey:o.key};for(
var r=["identify","goal","updateUserData","start","stop","refresh","show","hide","on"],
i={},n=0;n>r.length;n++){var a=r[n];i[a]=function(t){return function(){var
e=Array.prototype.slice.call(arguments);
window.Signalayer.cs.push({method:t,args:e})}}(a)}window.Signalayer.API=i;var
n=t.createElement(e),s=t.getElementsByTagName(e)[0];
n.type="text/javascript",
n.async=o.async,s.parentNode.insertBefore(n,s),n.src=o.url}(document,"script");
(function(d, s){
var t = d.createElement(s), e = d.getElementsByTagName(s)[0];
t.type = "text/javascript"; e.parentNode.insertBefore(t, e);
t.async = "async";
t.src = "https://cdn.tooltip.io/static/player.js?apiKey=sEF8shc4KSuqtHMx0ztmTFdcrQO0cAVpszZ9Y8hLfQ2";
})(document, "script");
</script>
<!--- Signalayer player -->
<!--- Tooltip player -->
</head>
<body>
<div id="spinner" class="component" style="z-index:999999;" data-path-start="M280,466c0,0.13-0.001,0.26-0.003,0.39c-0.002,0.134-0.004,0.266-0.007,0.396
<div id="spinner" class="component" data-path-start="M280,466c0,0.13-0.001,0.26-0.003,0.39c-0.002,0.134-0.004,0.266-0.007,0.396
C279.572,482.992,266.307,496,250,496h-2.125H51.625H50c-16.316,0-29.592-13.029-29.99-29.249c-0.003-0.13-0.006-0.261-0.007-0.393
C20.001,466.239,20,466.119,20,466l0,0c0-0.141,0.001-0.281,0.003-0.422C20.228,449.206,33.573,436,50,436h1.625h196.25H250
c16.438,0,29.787,13.222,29.997,29.608C279.999,465.738,280,465.869,280,466L280,466z" data-path-listen="M181,466c0,0.13-0.001,0.26-0.003,0.39c-0.002,0.134-0.004,0.266-0.007,0.396
@ -49,18 +63,5 @@ C120.001,466.239,120,466.119,120,466l0,0c0-0.141,0.001-0.281,0.003-0.422C120.228
c16.438,0,29.787,13.222,29.997,29.608C180.999,465.738,181,465.869,181,466L181,466z" data-path-player="M290,40c0,0.13-0.001,380.26-0.003,380.39c-0.002,0.134,0.006,24.479,0.003,24.609 c0,3.095-2.562,5.001-5,5.001h-27.125H41.625H15c-1.875,0-5-1.25-5-5.001c-0.003-0.13,0.004-24.509,0.003-24.641 C10.001,420.239,10,40.119,10,40l0,0c0-0.141-0.002-24.859,0-25c0,0,0-5,5-5h26.625h216.25H285c2.438,0,5,1.906,5,5 C290.002,15.13,290,39.869,290,40L290,40z">
<div class="button button--start uil-ring-css" style="transform:scale(1);"><div></div></div>
</div>
<div id="background" style="height: 100%; width: 100%; background-color: #0675A0; position: absolute; z-index:99999; top: 0;">
</div>
</body>
<!-- Fixes visibility issues with some apps that only update the badges while visible -->
<style>
.x-hidden-offsets {
visibility: visible!important;
position: absolute!important;
z-index: -10!important;
top: 0!important;
}
</style>
</html>

2
languages.js

@ -18,7 +18,7 @@ var deleteFolderRecursive = function(path) {
};
var crowdin = new Crowdin({
apiKey: '1e7c2453268af5e31f6ac8cf6044d48b',
apiKey: '',
endpointUrl: 'https://api.crowdin.net/api/project/rambox'
});

21
masterpassword.html

@ -9,31 +9,14 @@
<body>
<div style="background-image: url(resources/Icon.png);background-size:200px;width:200px;height:200px;position:relative;left:50%;margin-left:-100px;margin-top:100px;"></div>
<div style="color:#FFF;text-align:center;font-size:20px;font-family:Verdana;margin:30px 0;">Master Password</div>
<div style="text-align:center;margin:30px 0;"><input type="password" autofocus placeholder="Enter your password..." onpaste="handlePaste(event)" onkeyup="doValidate(event, this.value)" style="text-align:center;font-size:20px;font-family:Verdana;padding:10px;border: 0;" /><button onclick="onButtonClick()" style="height:44px;border:0;width:44px;position:relative;top:2px;font-size:30px;background-color:#053767;color:white;">&raquo;</button></div>
<div id='wrong-pass' style="color:crimson;text-align:center;font-size:20px;font-family:Verdana;margin:30px 0;visibility:hidden;">The password is incorrect. Try again...</div>
<div style="text-align:center;margin:30px 0;"><input type="password" autofocus placeholder="Enter your password..." onkeyup="doValidate(event, this.value)" style="text-align:center;font-size:20px;font-family:Verdana;padding:10px;border: 0;" /><button onclick="onButtonClick()" style="height:44px;border:0;width:44px;position:relative;top:2px;font-size:30px;background-color:#053767;color:white;">&raquo;</button></div>
<div style="text-align:center;margin-top:60px;"><a href="#" onclick="exit()" style="text-decoration:none;color:#4e9dbb;font-family:Arial;">Exit Rambox</a></div>
<script>
function handlePaste(e) {
var clipboardData, pastedData;
// Stop data actually being pasted into div
e.stopPropagation();
e.preventDefault();
// Get pasted data via clipboard API
clipboardData = e.clipboardData || window.clipboardData;
pastedData = clipboardData.getData('Text');
document.getElementsByTagName('input')[0].value += pastedData
// Do whatever with pasteddata
}
function doValidate(e, value) {
document.getElementById('wrong-pass').style.visibility = 'hidden'
if (e.keyCode == 13) {
const { ipcRenderer } = require('electron');
if ( !ipcRenderer.sendSync('validateMasterPassword', value) ) {
document.getElementsByTagName("input")[0].value = "";
document.getElementById('wrong-pass').style.visibility = 'visible'
alert('The password is incorrect. Try again...');
}
}
}

7220
package-lock.json generated

File diff suppressed because it is too large Load Diff

222
package.json

@ -1,103 +1,48 @@
{
"name": "Rambox",
"productName": "Rambox",
"version": "0.8.0",
"description": "Free and Open Source messaging and emailing app that combines common web applications into one.",
"main": "electron/main.js",
"repository": {
"type": "git",
"url": "https://github.com/ramboxapp/community-edition.git"
},
"bugs": {
"url": "https://github.com/ramboxapp/community-edition/issues"
},
"homepage": "https://rambox.app",
"keywords": [
"Rambox",
"messaging",
"app",
"slack",
"whatsapp",
"facebook",
"messenger",
"telegram",
"google",
"hangouts",
"skype"
],
"author": "Rambox LLC <support@rambox.app>",
"license": "GPL-3.0",
"private": true,
"scripts": {
"start": "electron electron/main.js",
"start:debug": "electron electron/main.js --enable-logging",
"dev": "electron electron/main.js",
"test": "./node_modules/.bin/mocha test/tests/**/*.spec.js",
"sencha:clean": "rm -rf ./build/production",
"sencha:compile": "sencha app build && npm --prefix ./build/production/Rambox/ install ./build/production/Rambox/",
"sencha:compile:build": "sencha app build",
"sencha:compile": "sencha app build && cp app/package.json build/production/Rambox/ && npm --prefix ./build/production/Rambox/ install ./build/production/Rambox/",
"sencha:compile:build": "sencha app build && cp app/package.json build/production/Rambox/ && cp -R build/production/Rambox/* ../rambox-build",
"clean": "rm -rf ./dist",
"clean:osx": "rm -rf ./dist/Rambox-darwin-*",
"clean:win": "rm -rf ./dist/Rambox-win32-*",
"pack": "npm run pack:osx && npm run pack:win",
"pack:osx": "npm run pack:osx64 && npm run pack:osxarm64",
"pack:osx64": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=darwin --arch=x64 --icon=resources/installer/Icon.icns --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:osxarm64": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=darwin --arch=arm64 --icon=resources/installer/Icon.icns --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:osx": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=darwin --arch=x64 --version=1.3.4 --icon=resources/installer/Icon.icns --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:win": "npm run pack:win32 && npm run pack:win64",
"pack:win32": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=win32 --arch=ia32 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=32-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:win64": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=win32 --arch=x64 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:win32": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=win32 --arch=ia32 --version=1.3.4 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=32-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:win64": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=win32 --arch=x64 --version=1.3.4 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:linux": "npm run pack:linux32 && npm run pack:linux64",
"pack:linux32": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=linux --arch=ia32 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:linux64": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=linux --arch=x64 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:linux32": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=linux --arch=ia32 --version=1.3.4 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"pack:linux64": "electron-packager \"./build/production/Rambox/\" \"Rambox\" --out=dist --platform=linux --arch=x64 --version=1.3.4 --icon=resources/installer/Icon.ico --app-version=0.2.0 --build-version=64-bit --version-string.CompanyName=\"Rambox\" --version-string.ProductName=\"Rambox\" --asar --prune --overwrite",
"build": "npm run build:linux && npm run build:osx && npm run build:win",
"build:osx": "electron-builder --macos --universal",
"build:osx64": "electron-builder --macos --x64",
"build:osxarm64": "electron-builder --macos --arm64",
"build:linux": "electron-builder --linux --publish=onTagOrDraft",
"build:linux32": "electron-builder --linux --ia32 --publish=onTagOrDraft",
"build:linux64": "electron-builder --linux --x64 --publish=onTagOrDraft",
"build:win": "electron-builder --win --ia32 --x64",
"build:win32": "electron-builder --win --ia32",
"build:win64": "electron-builder --win --x64",
"setup:osx": "npm run sencha:clean && npm run sencha:compile && npm run clean:osx && npm run pack:osx && npm run build:osx && npm run build:osx64 && npm run build:osxarm64",
"build:osx": "build --macos",
"build:linux": "npm run build:linux32 && npm run build:linux64",
"build:linux32": "build --linux --ia32",
"build:linux64": "build --linux --x64",
"build:win32": "build --win --ia32",
"build:win64": "build --win --x64",
"setup:osx": "npm run sencha:clean && npm run sencha:compile && npm run clean:osx && npm run pack:osx && npm run build:osx",
"setup:win": "npm run sencha:clean && npm run sencha:compile && npm run clean:win && npm run pack:win && npm run build:win",
"all:win": "npm run sencha:clean && npm run sencha:compile && npm run clean:win && npm run pack:win && npm run zip:win32 && npm run zip:win64 && npm run build:win",
"all:linux": "npm run sencha:clean && npm run sencha:compile && npm run build:linux",
"translations:download": "node languages.js download",
"translations:generate": "node languages.js generate"
"all:linux": "npm run sencha:clean && npm run sencha:compile && npm run build:linux"
},
"build": {
"productName": "Rambox",
"appId": "com.grupovrs.ramboxce",
"afterSign": "resources/installer/notarize.js",
"appId": "com.saenzramiro.rambox",
"asar": true,
"electronVersion": "13.6.3",
"electronDownload": {
"version": "13.6.3"
},
"mac": {
"category": "public.app-category.productivity",
"artifactName": "Rambox-${version}-mac-${arch}.${ext}",
"target": [
{
"target": "default",
"arch": [
"universal"
]
}
],
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "resources/installer/entitlements.mac.plist",
"entitlementsInherit": "resources/installer/entitlements.mac.plist",
"extendInfo": {
"NSMicrophoneUsageDescription": "Apps inside Rambox CE may need access to your microphone. Please, grant access to have a better experience.",
"NSCameraUsageDescription": "Apps inside Rambox CE may need access to your camera. Please, grant access to have a better experience."
}
"default"
]
},
"dmg": {
"title": "Rambox",
"iconSize": 128,
"sign": false,
"contents": [
{
"x": 355,
@ -112,136 +57,55 @@
}
]
},
"squirrelWindows": {
"iconUrl": "https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/Icon.ico"
},
"win": {
"publisherName": "Rambox LLC",
"artifactName": "Rambox-${version}-win-${arch}.${ext}",
"target": [
"nsis",
"squirrel",
"zip"
]
},
"nsis": {
"deleteAppDataOnUninstall": true,
"oneClick": false,
"perMachine": false,
"runAfterFinish": true
},
"snap": {
"publish": [
{
"provider": "github"
}
],
"plugs": [
"default",
"camera",
"audio-record",
"audio-playback",
"removable-media",
"raw-usb",
"u2f-devices",
"cups-control"
]
},
"linux": {
"icon": "resources/installer/icons",
"category": "Network",
"category": "Office",
"desktop": {
"Terminal": "false",
"Type": "Application",
"Categories": "GTK;GNOME;Network;Email;Chat;InstantMessaging;"
"Categories": "GTK;GNOME;Utility;Office;Email;Chat;InstantMessaging;"
},
"artifactName": "Rambox-${version}-linux-${arch}.${ext}",
"executableArgs": [
"--no-sandbox"
],
"target": [
{
"target": "snap",
"arch": [
"x64"
]
},
{
"target": "AppImage",
"arch": [
"x64",
"ia32"
]
},
{
"target": "deb",
"arch": [
"x64",
"ia32"
]
},
{
"target": "rpm",
"arch": [
"x64",
"ia32"
]
},
{
"target": "zip",
"arch": [
"x64",
"ia32"
]
},
{
"target": "tar.gz",
"arch": [
"x64",
"ia32"
]
}
"AppImage",
"deb",
"rpm",
"zip",
"tar.gz"
]
},
"directories": {
"buildResources": "resources/installer/",
"output": "dist/"
},
"publish": [
{
"provider": "github",
"owner": "ramboxapp",
"repo": "community-edition",
"vPrefixedTagName": false
}
]
"output": "dist/",
"app": "build/production/Rambox/"
}
},
"devDependencies": {
"asar": "0.12.4",
"asar": "^0.12.4",
"chai": "3.5.0",
"crowdin": "1.0.0",
"csvjson": "4.3.3",
"electron": "13.6.3",
"electron-builder": "22.14.13",
"electron-notarize": "1.0.0",
"electron-packager": "15.1.0",
"mocha": "5.2.0",
"spectron": "^15.0.0"
"electron": "1.7.5",
"electron-builder": "^17.10.0",
"electron-builder-squirrel-windows": "15.0.0",
"electron-squirrel-startup": "^1.0.0",
"mocha": "3.2.0",
"spectron": "3.4.0"
},
"dependencies": {
"@exponent/electron-cookies": "2.0.0",
"auto-launch-patched": "5.0.2",
"crypto": "1.0.1",
"electron-contextmenu-wrapper": "git+https://github.com/ramboxapp/electron-contextmenu-wrapper.git",
"electron-is-dev": "1.2.0",
"electron-log": "4.3.0",
"electron-store": "6.0.1",
"electron-updater": "4.5.2",
"is-online": "8.2.0",
"mime": "2.3.1",
"mousetrap": "1.6.3",
"request": "2.88.0",
"request-promise": "4.2.2",
"electron-config": "0.2.1",
"electron-is-dev": "^0.1.2",
"mime": "^1.4.0",
"rimraf": "2.6.1",
"tmp": "0.0.28"
},
"volta": {
"node": "14.16.1"
}
}

1
packages/local/rambox-default-theme/Readme.md

@ -1 +1,2 @@
# rambox-default-theme - Read Me

47
packages/local/rambox-default-theme/examples/Readme.md

@ -1,45 +1,38 @@
# rambox-default-theme/examples
This folder contains example applications demonstrating this package.
Each of these applications will be built as part of the package build:
This folder contains example applications demonstrating this package. Each of
these applications will be built as part of the package build:
```bash
cd /path/to/package
sencha package build
```
cd /path/to/package
sencha package build
As applications, they can also be built individually:
```bash
cd /path/to/package/examples/example-app
sencha app build
```
cd /path/to/package/examples/example-app
sencha app build
Or you can build all examples as a group:
```bash
cd /path/to/package
sencha ant examples
```
cd /path/to/package
sencha ant examples
The ideal location for the example builds to reside is the `"./build"` folder:
```text
/path/to/package/
src/
resources/
...
examples/
example-app/
other-example/
...
build/
/path/to/package/
src/
resources/
...
examples/
example-app/
other-example/
```
...
build/
resources/
examples/
example-app/
other-example/
This can be specified in the `".sencha/app/build.properties"` file for the example applications:
This can be specified in the `".sencha/app/build.properties"` file for the
example applications:
`build.dir=${package.build.dir}/examples/${app.name}`
build.dir=${package.build.dir}/examples/${app.name}

8
packages/local/rambox-default-theme/sass/Readme.md

@ -2,8 +2,6 @@
This folder contains SASS files of various kinds, organized in sub-folders:
```text
rambox-default-theme/sass/etc
rambox-default-theme/sass/src
rambox-default-theme/sass/var
```
rambox-default-theme/sass/etc
rambox-default-theme/sass/src
rambox-default-theme/sass/var

4
packages/local/rambox-default-theme/sass/etc/Readme.md

@ -1,4 +1,4 @@
# rambox-default-theme/sass/etc
This folder contains miscellaneous SASS files.
Unlike `"rambox-default-theme/sass/etc"`, these files need to be used explicitly.
This folder contains miscellaneous SASS files. Unlike `"rambox-default-theme/sass/etc"`, these files
need to be used explicitly.

12
packages/local/rambox-default-theme/sass/etc/all.scss

@ -272,15 +272,3 @@ body {
color: #FFF !important;
}
}
.x-tab-bar-default.dontdisturb {
background-color: #c3c3c3 !important;
.x-tab {
background-color: #c3c3c3;
}
}
.deprecation {
background-color: #E6A23C !important;
color: #FFF;
}

4
packages/local/rambox-default-theme/sass/src/Readme.md

@ -1,4 +1,4 @@
# rambox-default-theme/sass/src
This folder contains SASS sources that mimic the component-class hierarchy.
These files are gathered in to a build of the CSS based on classes that are used by the build.
This folder contains SASS sources that mimic the component-class hierarchy. These files
are gathered in to a build of the CSS based on classes that are used by the build.

3
packages/local/rambox-default-theme/src/Readme.md

@ -1,3 +1,4 @@
# rambox-default-theme/src
This folder contains source code that will automatically be added to the classpath when the package is used.
This folder contains source code that will automatically be added to the classpath when
the package is used.

4
resources/Readme.md

@ -0,0 +1,4 @@
# Rambox/resources
This folder contains resources (such as images) needed by the application. This file can
be removed if not needed.

BIN
resources/icons/sococo.png → resources/auth0.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 45 KiB

BIN
resources/icons/aim.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
resources/icons/airdroid.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

BIN
resources/icons/allo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
resources/icons/amium.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
resources/icons/androidmessages.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

BIN
resources/icons/aol.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
resources/icons/awsworkmail.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

BIN
resources/icons/bearychat.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 70 KiB

BIN
resources/icons/bip.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

BIN
resources/icons/calendar.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

BIN
resources/icons/ciscospark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
resources/icons/cliq.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

BIN
resources/icons/clocktweets.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
resources/icons/converse.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

BIN
resources/icons/crisp.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 68 KiB

BIN
resources/icons/dasher.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
resources/icons/devrant.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
resources/icons/dingtalk.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 56 KiB

BIN
resources/icons/discord.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
resources/icons/drift.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 52 KiB

BIN
resources/icons/duo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

BIN
resources/icons/element.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

BIN
resources/icons/facebook.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
resources/icons/fastmail.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save