diff --git a/composer.json b/composer.json index 0a697bf..642320a 100644 --- a/composer.json +++ b/composer.json @@ -1,22 +1,14 @@ { - "name": "vinceg/usps-php-api", + "name": "paperscissors/usps-php-api", "type": "library", - "description": "PHP wrapper for USPS Web Tools API", + "description": "Fork of PHP wrapper for USPS Web Tools API with quick issue fixes; originally by Vincent Gabriel et al.", "keywords": ["usps", "api"], "license": "MIT", "authors": [ { - "name": "Vincent Gabriel", - "homepage": "http://vadimg.com", + "name": "Tom Filepp", + "homepage": "https://paperscissorsandglue.com", "role": "Developer" - }, - { - "name": "Alexander Reiff", - "role": "Developer" - }, - { - "name": "Ryan Hayle", - "role": "Developer" } ], "require": { diff --git a/helpers/context_menu.js b/helpers/context_menu.js new file mode 100644 index 0000000..490844c --- /dev/null +++ b/helpers/context_menu.js @@ -0,0 +1,55 @@ +// This gives you default context menu (cut, copy, paste) +// in all input fields and textareas across your app. + +import { remote } from 'electron'; + +const Menu = remote.Menu; +const MenuItem = remote.MenuItem; + +const isAnyTextSelected = () => { + return window.getSelection().toString() !== ''; +}; + +const cut = new MenuItem({ + label: 'Cut', + click: () => { + document.execCommand('cut'); + }, +}); + +const copy = new MenuItem({ + label: 'Copy', + click: () => { + document.execCommand('copy'); + }, +}); + +const paste = new MenuItem({ + label: 'Paste', + click: () => { + document.execCommand('paste'); + }, +}); + +const normalMenu = new Menu(); +normalMenu.append(copy); + +const textEditingMenu = new Menu(); +textEditingMenu.append(cut); +textEditingMenu.append(copy); +textEditingMenu.append(paste); + +document.addEventListener('contextmenu', (event) => { + switch (event.target.nodeName) { + case 'TEXTAREA': + case 'INPUT': + event.preventDefault(); + textEditingMenu.popup(remote.getCurrentWindow()); + break; + default: + if (isAnyTextSelected()) { + event.preventDefault(); + normalMenu.popup(remote.getCurrentWindow()); + } + } +}, false); diff --git a/helpers/external_links.js b/helpers/external_links.js new file mode 100644 index 0000000..db7215f --- /dev/null +++ b/helpers/external_links.js @@ -0,0 +1,40 @@ +// Convenient way for opening links in external browser, not in the app. +// Useful especially if you have a lot of links to deal with. +// +// Usage: +// +// Every link with class ".js-external-link" will be opened in external browser. +// google +// +// The same behaviour for many links can be achieved by adding +// this class to any parent tag of an anchor tag. +//
+ +import { shell } from 'electron'; + +const supportExternalLinks = (event) => { + let href; + let isExternal = false; + + const checkDomElement = (element) => { + if (element.nodeName === 'A') { + href = element.getAttribute('href'); + } + if (element.classList.contains('js-external-link')) { + isExternal = true; + } + if (href && isExternal) { + shell.openExternal(href); + event.preventDefault(); + } else if (element.parentElement) { + checkDomElement(element.parentElement); + } + }; + + checkDomElement(event.target); +}; + +document.addEventListener('click', supportExternalLinks, false); diff --git a/helpers/window.js b/helpers/window.js new file mode 100644 index 0000000..7df3f30 --- /dev/null +++ b/helpers/window.js @@ -0,0 +1,82 @@ +// This helper remembers the size and position of your windows (and restores +// them in that place after app relaunch). +// Can be used for more than one window, just construct many +// instances of it and give each different name. + +import { app, BrowserWindow, screen } from 'electron'; +import jetpack from 'fs-jetpack'; + +export default (name, options) => { + const userDataDir = jetpack.cwd(app.getPath('userData')); + const stateStoreFile = `window-state-${name}.json`; + const defaultSize = { + width: options.width, + height: options.height, + }; + let state = {}; + let win; + + const restore = () => { + let restoredState = {}; + try { + restoredState = userDataDir.read(stateStoreFile, 'json'); + } catch (err) { + // For some reason json can't be read (might be corrupted). + // No worries, we have defaults. + } + return Object.assign({}, defaultSize, restoredState); + }; + + const getCurrentPosition = () => { + const position = win.getPosition(); + const size = win.getSize(); + return { + x: position[0], + y: position[1], + width: size[0], + height: size[1], + }; + }; + + const windowWithinBounds = (windowState, bounds) => { + return windowState.x >= bounds.x + && windowState.y >= bounds.y + && windowState.x + windowState.width <= bounds.x + bounds.width + && windowState.y + windowState.height <= bounds.y + bounds.height; + }; + + const resetToDefaults = () => { + const bounds = screen.getPrimaryDisplay().bounds; + return Object.assign({}, defaultSize, { + x: (bounds.width - defaultSize.width) / 2, + y: (bounds.height - defaultSize.height) / 2, + }); + }; + + const ensureVisibleOnSomeDisplay = (windowState) => { + const visible = screen.getAllDisplays().some((display) => { + return windowWithinBounds(windowState, display.bounds); + }); + if (!visible) { + // Window is partially or fully not visible now. + // Reset it to safe defaults. + return resetToDefaults(); + } + return windowState; + }; + + const saveState = () => { + if (!win.isMinimized() && !win.isMaximized()) { + Object.assign(state, getCurrentPosition()); + } + userDataDir.write(stateStoreFile, state, { atomic: true }); + }; + + state = ensureVisibleOnSomeDisplay(restore()); + + win = new BrowserWindow(Object.assign({}, options, state)); + + win.on('close', saveState); + + return win; +}; diff --git a/src/USPSBase.php b/src/USPSBase.php index 83653ba..a19f261 100644 --- a/src/USPSBase.php +++ b/src/USPSBase.php @@ -211,7 +211,7 @@ protected function doRequest($ch = null) // Find the error number $errorInfo = $this->getValueByKey($arrayResponse, 'Error'); - if ($errorInfo) { + if (isset($errorInfo['Number'], $errorInfo['Description'])) { $this->setErrorCode($errorInfo['Number']); $this->setErrorMessage($errorInfo['Description']); }