Merge conflicts resolved

This commit is contained in:
Manoj Vivek 2020-10-18 21:11:16 +05:30
commit 02cd39304a
2674 changed files with 817 additions and 468776 deletions

View file

@ -352,6 +352,24 @@
"contributions": [
"code"
]
},
{
"login": "sidthesloth92",
"name": "Dinesh Balaji",
"avatar_url": "https://avatars3.githubusercontent.com/u/4656109?v=4",
"profile": "http://dbwriteups.wordpress.com",
"contributions": [
"code"
]
},
{
"login": "med1001",
"name": "MedBMoussa",
"avatar_url": "https://avatars3.githubusercontent.com/u/26111211?v=4",
"profile": "https://github.com/med1001",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 5,

37
.github/ISSUE_TEMPLATE/01-bug-report.md vendored Normal file
View file

@ -0,0 +1,37 @@
---
name: "\U0001F41E Bug report"
about: Report a bug in Responsively
---
<!-- 🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
Hi there! 😄
To expedite issue processing please search open and closed issues before submitting a new one. Existing issues often contain information about workarounds, resolution, or progress updates.
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅 -->
# 🐞 bug report
### ✍️ Description
<!-- A clear and concise description of the problem. -->
### 🕵🏼‍♂️ Is this a regression?
<!-- Did this behavior use to work in the previous version? -->
### 🔬 Minimal Reproduction
<!-- Clear steps to re-produce the issue. -->
### 🌍 Your Environment
<!-- Press `Ctrl/Cmd + F1` and paste it here. -->
<pre><code>
</code></pre>
### 🔥 Exception or Error or Screenshot
<pre><code>
</code></pre>

View file

@ -0,0 +1,18 @@
---
name: "\U0001F680 Feature request"
about: Suggest a feature for Responsively.
---
# 🚀 Feature Request
### 📝 Description
<!-- A clear and concise description of the problem or missing capability. -->
### ✨ Describe the solution you'd like
<!-- If you have a solution in mind, please describe it. -->
### ✍️ Describe alternatives you've considered
<!-- Have you considered any alternative solutions or workarounds? -->

13
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,13 @@
# ✨ Pull Request
### 📓 Referenced Issue
<!-- Please link the related issue. Use # before the issue number and use the verbs 'fixes', 'resolves' to auto-link it, for eg, Fixes: #&lt;issue-number&gt; -->
### About the PR
<!-- Please provide a description of your solution if it is not clear in the related issue or if the PR has a breaking change. If there is an interesting topic to discuss or you have questions or there is an issue with electron or another library that you have used. -->
### 🖼️ Testing Scenarios / Screenshots
<!-- Please include screenshots or gif to showcase the final output. Also, try to explain the testing you did to validate your change. -->

1
.github/opencollective.yml vendored Normal file
View file

@ -0,0 +1 @@
collective: responsively

View file

@ -37,7 +37,7 @@
</p>
<p align="center">
Download Now(free!): <a href="https://responsively.app" target="_blank">
Download Now (free!): <a href="https://responsively.app" target="_blank">
responsively.app
</a>
</p>
@ -63,11 +63,16 @@ Please visit the website to know more about the application - https://responsive
## Download
The application is available for Mac, Windows and Linux platforms. Please download it from here - https://github.com/responsively-org/responsively-app/releases
Alternatively, MacOS users can use brew to install it:
Alternatively, MacOS users can use [`brew`](https://formulae.brew.sh/cask/responsively) to install it:
```bash
brew cask install responsively
```
Also, Windows users can use [`chocolatey`](https://chocolatey.org/packages/responsively/) to install it:
```bash
choco install responsively
```
Follow on Twitter for future updates - [![Twitter Follow](https://img.shields.io/twitter/follow/ResponsivelyApp?style=social)](https://twitter.com/ResponsivelyApp)
## Issues
@ -147,6 +152,8 @@ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/doc
<td align="center"><a href="http://ruisaraiva.dev"><img src="https://avatars2.githubusercontent.com/u/7356098?v=4" width="100px;" alt=""/><br /><sub><b>Rui Saraiva</b></sub></a><br /><a href="https://github.com/responsively-org/responsively-app/commits?author=ruisaraiva19" title="Code">💻</a></td>
<td align="center"><a href="http://www.bakirci.nl"><img src="https://avatars2.githubusercontent.com/u/9880089?v=4" width="100px;" alt=""/><br /><sub><b>Mehmet Bakirci</b></sub></a><br /><a href="https://github.com/responsively-org/responsively-app/commits?author=MBakirci" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/JLambertazzo"><img src="https://avatars0.githubusercontent.com/u/42924425?v=4" width="100px;" alt=""/><br /><sub><b>Julien Bertazzo Lambert</b></sub></a><br /><a href="https://github.com/responsively-org/responsively-app/commits?author=JLambertazzo" title="Code">💻</a></td>
<td align="center"><a href="http://dbwriteups.wordpress.com"><img src="https://avatars3.githubusercontent.com/u/4656109?v=4" width="100px;" alt=""/><br /><sub><b>Dinesh Balaji</b></sub></a><br /><a href="https://github.com/responsively-org/responsively-app/commits?author=sidthesloth92" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/med1001"><img src="https://avatars3.githubusercontent.com/u/26111211?v=4" width="100px;" alt=""/><br /><sub><b>MedBMoussa</b></sub></a><br /><a href="https://github.com/responsively-org/responsively-app/commits?author=med1001" title="Code">💻</a></td>
</tr>
</table>

View file

@ -1,5 +1,4 @@
import React, {Fragment} from 'react';
import {Switch, Route} from 'react-router';
import Box from '@material-ui/core/Box';
import Paper from '@material-ui/core/Paper';
import {makeStyles} from '@material-ui/core/styles';
@ -9,15 +8,13 @@ import LeftIconsPaneContainer from './containers/LeftIconsPaneContainer';
import StatusBarContainer from './containers/StatusBarContainer';
import DevToolResizerContainer from './containers/DevToolResizerContainer';
const Routes = () => {
const AppContent = () => {
const classes = useStyles();
return (
<Fragment>
<Paper elevation={0} className={classes.root}>
<div className={classes.contentColumn}>
<Switch>
<Route path={routes.HOME} component={Browser} />
</Switch>
<Browser />
</div>
</Paper>
<StatusBarContainer />
@ -69,4 +66,4 @@ const useStyles = makeStyles(theme => ({
},
}));
export default Routes;
export default AppContent;

View file

@ -15,9 +15,11 @@ import {
DELETE_STORAGE,
ADDRESS_CHANGE,
STOP_LOADING,
TOGGLE_DEVICE_DESIGN_MODE_STATE,
} from '../constants/pubsubEvents';
import {getBounds, getDefaultDevToolsWindowSize} from '../reducers/browser';
import {DEVTOOLS_MODES} from '../constants/previewerLayouts';
import {normalizeZoomLevel} from '../utils/browserUtils';
export const NEW_ADDRESS = 'NEW_ADDRESS';
export const NEW_PAGE_META_FIELD = 'NEW_PAGE_META_FIELD';
@ -44,6 +46,10 @@ export const NEW_FOCUSED_DEVICE = 'NEW_FOCUSED_DEVICE';
export const TOGGLE_ALL_DEVICES_MUTED = 'TOGGLE_ALL_DEVICES_MUTED';
export const TOGGLE_DEVICE_MUTED = 'TOGGLE_DEVICE_MUTED';
export const NEW_THEME = 'NEW_THEME';
export const TOGGLE_ALL_DEVICES_DESIGN_MODE = 'TOGGLE_ALL_DEVICES_DESIGN_MODE';
export const TOGGLE_DEVICE_DESIGN_MODE = 'TOGGLE_DEVICE_DESIGN_MODE';
export const SET_HEADER_VISIBILITY = 'SET_HEADER_VISIBILITY';
export const SET_LEFT_PANE_VISIBILITY = 'SET_LEFT_PANE_VISIBILITY';
export function newAddress(address) {
return {
@ -208,6 +214,19 @@ export function toggleDeviceMuted(deviceId, isMuted) {
};
}
export function toggleAllDevicesDesignMode() {
return {
type: TOGGLE_ALL_DEVICES_DESIGN_MODE,
};
}
export function toggleDeviceDesignMode(deviceId) {
return {
type: TOGGLE_DEVICE_DESIGN_MODE,
deviceId,
};
}
export function onAddressChange(newURL, force) {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
@ -249,12 +268,13 @@ export function onZoomChange(newLevel) {
const {
browser: {zoomLevel},
} = getState();
const normalizedZoomLevel = normalizeZoomLevel(newLevel);
if (newLevel === zoomLevel) {
if (normalizedZoomLevel === zoomLevel) {
return;
}
dispatch(newZoomLevel(newLevel));
dispatch(newZoomLevel(normalizedZoomLevel));
};
}
@ -720,6 +740,23 @@ export function onDeviceMutedChange(deviceId, isMuted) {
};
}
export function onToggleAllDeviceDesignMode() {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
browser: {allDevicesInDesignMode},
} = getState();
const next = !allDevicesInDesignMode;
pubsub.publish(TOGGLE_DEVICE_DESIGN_MODE_STATE, [{designMode: next}]);
dispatch(toggleAllDevicesDesignMode());
};
}
export function onToggleDeviceDesignMode(deviceId) {
return (dispatch: Dispatch, getState: RootStateType) => {
dispatch(toggleDeviceDesignMode(deviceId));
};
}
export function toggleInspector() {
return (dispatch: Dispatch, getState: RootStateType) => {
const {
@ -848,3 +885,25 @@ export function setTheme(theme) {
theme,
};
}
/**
* Shows/Hides the top control pane.
* @param {boolean} isVisible Shows the top pane when true and hides when false.
*/
export function setHeaderVisibility(isVisible: boolean) {
return {
type: SET_HEADER_VISIBILITY,
isVisible,
};
}
/**
* Shows/Hides the left control pane.
* @param {boolean} isVisible Shows the left control pane when true and hides when false.
*/
export function setLeftPaneVisibility(isVisible: boolean) {
return {
type: SET_LEFT_PANE_VISIBILITY,
isVisible,
};
}

View file

@ -3,7 +3,6 @@ import type {Dispatch, GetState} from '../reducers/types';
import {
SET_NETWORK_TROTTLING_PROFILE,
CLEAR_NETWORK_CACHE,
SET_NETWORK_PROXY_PROFILE,
} from '../constants/pubsubEvents';
import {convertToProxyConfig, proxyRuleToString} from '../utils/proxyUtils';
import {ipcRenderer} from 'electron';

View file

@ -11,7 +11,6 @@ import logo from '../../../resources/logo.svg';
function updateNotificationStatus(id, action) {
const notifications = settings.get(APP_NOTIFICATION) || [];
const commonClasses = useCommonStyles();
const notificationStatusObject = {
id,
@ -39,6 +38,8 @@ function checkIfInteracted(id) {
const AppNotification = () => {
const [notificationInteracted, setNotificationInteracted] = useState(false);
const [data, setData] = useState(null);
const commonClasses = useCommonStyles();
useEffect(() => {
if (process.env.NODE_ENV === 'development') {
return;

View file

@ -10,6 +10,7 @@
padding: 10px 20px 20px 20px;
min-width: 250px;
max-width: 320px;
color: #f8f8f8;
}
.titleContainer {

View file

@ -9,6 +9,7 @@ import CloseIcon from '@material-ui/icons/Close';
import AddIcon from '@material-ui/icons/Add';
import Dialog from '@material-ui/core/Dialog';
import AppBar from '@material-ui/core/AppBar';
import Alert from '@material-ui/lab/Alert';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import {DragDropContext, Droppable, Draggable} from 'react-beautiful-dnd';
@ -16,6 +17,7 @@ import LightBulbIcon from '../icons/LightBulb';
import DeviceList from './DeviceList';
import AddDeviceContainer from '../../containers/AddDeviceContainer';
import ErrorBoundary from '../ErrorBoundary';
import {recommendedMaxNumberOfDevices} from '../../utils/deviceManagerUtils';
import styles from './styles.css';
@ -29,6 +31,8 @@ function DeviceManager(props) {
inactiveFiltered: [],
});
const [maxDevicesWarning, setMaxDevicesWarning] = useState(false);
useEffect(() => {
const activeDevices = props.browser.devices;
const activeDevicesById = activeDevices.reduce((acc, val) => {
@ -36,6 +40,8 @@ function DeviceManager(props) {
return acc;
}, {});
setMaxDevicesWarning(activeDevices.length >= recommendedMaxNumberOfDevices);
const currentInactiveDevicesById = devices.inactive.reduce((acc, val) => {
acc[val.id] = val;
return acc;
@ -104,6 +110,7 @@ function DeviceManager(props) {
const updateDevices = devices => {
const active = [...devices.active];
const inactive = [...devices.inactive];
setMaxDevicesWarning(active.length >= recommendedMaxNumberOfDevices);
setDevices({active, inactive});
props.setActiveDevices(active);
};
@ -115,7 +122,10 @@ function DeviceManager(props) {
color="primary"
aria-label="upload picture"
component="span"
onClick={() => setOpen(true)}
onClick={() => {
props.onDevToolsClose(null, true);
setOpen(true);
}}
className={styles.editButton}
>
Customize
@ -133,6 +143,12 @@ function DeviceManager(props) {
</Toolbar>
</AppBar>
<div className={styles.container}>
{maxDevicesWarning && (
<Alert severity="warning" className={classes.maxDevicesWarning}>
Adding more than {recommendedMaxNumberOfDevices} devices may
slow down the system.
</Alert>
)}
<Typography variant="body1" className={classes.toolTip}>
<span></span>Drag and drop the devices across to re-order them.
</Typography>
@ -190,6 +206,10 @@ const useStyles = makeStyles(theme => ({
color: theme.palette.text.primary,
width: 'fit-content',
},
maxDevicesWarning: {
position: 'absolute',
top: '80px',
},
}));
export default DeviceManager;

View file

@ -69,6 +69,7 @@ class ErrorBoundary extends React.Component {
const styles = theme => ({
errorBoundaryContainer: {
background: theme.palette.background.default,
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',

View file

@ -12,23 +12,35 @@ import InputAdornment from '@material-ui/core/InputAdornment';
import {remote, ipcRenderer} from 'electron';
import cx from 'classnames';
import {
makeStyles,
Popper,
Fade,
Paper,
Typography,
ClickAwayListener,
} from '@material-ui/core';
import {useTheme} from '@material-ui/core/styles';
import {useTheme, makeStyles} from '@material-ui/core/styles';
import styles from './styles.css';
import useCommonStyles from '../useCommonStyles';
import helpScreenshot from './help-screenshot.png';
const useStyles = makeStyles({
const useStyles = makeStyles(theme => ({
adornedEnd: {
paddingRight: 0,
},
});
extensionsHelp: {
display: 'flex',
flexDirection: 'column',
width: 500,
padding: 12,
color: 'white',
borderRadius: 4,
background: theme.palette.mode({
light: theme.palette.secondary.main,
dark: '#313131',
}),
boxShadow: '3px 3px 6px 1px black',
},
}));
export default function ExtensionsManager({triggerNavigationReload}) {
const {BrowserWindow} = remote;
@ -114,22 +126,25 @@ export default function ExtensionsManager({triggerNavigationReload}) {
return (
<>
<Popper open={helpOpen} anchorEl={anchorEl} placement="bottom" transition>
<Popper
open={helpOpen}
anchorEl={anchorEl}
placement="bottom-start"
transition
>
{({TransitionProps}) => (
<ClickAwayListener onClickAway={toggleHelp}>
<Fade {...TransitionProps} timeout={350}>
<Paper>
<div className={styles.extensionsHelp}>
<p className={cx(styles.extensionsHelpText)}>
Find the extension on Chrome Web Store and copy the
extension ID from the address bar(as shown below).
</p>
<img
className={styles.extensionsHelpImg}
src={helpScreenshot}
/>
</div>
</Paper>
<div className={classes.extensionsHelp}>
<p className={cx(styles.extensionsHelpText)}>
Find the extension on Chrome Web Store and copy the extension
ID from the address bar(as shown below).
</p>
<img
className={styles.extensionsHelpImg}
src={helpScreenshot}
/>
</div>
</Fade>
</ClickAwayListener>
)}

View file

@ -49,19 +49,9 @@
margin-top: 12px !important;
}
.extensionsHelp {
display: flex;
flex-direction: column;
width: 500px;
padding: 12px;
color: white;
border-radius: 4px;
background: #252526;
box-shadow: 3px 3px 6px 1px black;
}
.extensionsHelpText {
font-size: 12px;
margin-top: 0;
}
.extensionsHelpImg {

View file

@ -10,14 +10,15 @@ import PermissionPopup from '../PermissionPopup';
import NavigationControlsContainer from '../../containers/NavigationControlsContainer';
import BookmarksBar from '../../containers/BookmarksBarContainer';
import AppNotification from '../AppNotification/AppNotification';
import Logo from '../icons/Logo';
import ZenButton from '../ZenButton';
import cx from 'classnames';
const Header = () => {
const Header = props => {
const classes = useStyles();
return (
<div className={classes.container}>
<div className={cx([classes.container, {zenMode: !props.isHeaderVisible}])}>
<div className={classes.firstRow}>
<Logo className={classes.logo} width={40} height={40} />
<Grid
@ -52,23 +53,46 @@ const Header = () => {
pauseOnHover
toastClassName={classes.darkToast}
/>
<AppNotification />
<ZenButton
active={!props.isHeaderVisible}
onClick={() => props.setHeaderVisibility(!props.isHeaderVisible)}
/>
</div>
);
};
const useStyles = makeStyles(theme => ({
container: {
position: 'relative',
background: theme.palette.background.l1,
display: 'flex',
flexDirection: 'column',
width: '100%',
padding: os.platform() === 'darwin' ? '20px 0 5px' : '0 0 0',
padding: os.platform() === 'darwin' ? '0 0 5px' : '0 0 0',
boxShadow: `0 ${theme.palette.mode({
light: '0px',
dark: '3px',
})} 5px rgba(0, 0, 0, 0.35)`,
zIndex: 500,
transform: 'translateY(0)',
transition: 'transform .1s ease-out',
'& .zenButton': {
background: theme.palette.background.l1,
display: 'none',
position: 'absolute',
bottom: '0px',
left: '50%',
transform: 'translate(-50%, 100%)',
},
'&:hover .zenButton': {
display: 'flex',
},
'&.zenMode': {
transform: 'translateY(-100%)',
},
'&.zenMode .zenButton': {
display: 'flex',
},
},
firstRow: {
display: 'flex',

View file

@ -0,0 +1,20 @@
import React from 'react';
import {makeStyles} from '@material-ui/core/styles';
/**
* Application toolbar that appears at the top of a window.
*/
const HorizontalSpacer = () => {
const classes = useStyles();
return <div className={classes.container} />;
};
const useStyles = makeStyles(theme => ({
container: {
background: theme.palette.background.l1,
padding: '11px 0',
zIndex: '10',
},
}));
export default HorizontalSpacer;

View file

@ -9,6 +9,7 @@ import NetworkIcon from '../icons/Network';
import Logo from '../icons/Logo';
import Gift from '../icons/Gift';
import Headway from '../Headway';
import ZenButton from '../ZenButton';
import styles from './styles.css';
import useCommonStyles from '../useCommonStyles';
@ -23,14 +24,38 @@ import {makeStyles} from '@material-ui/core/styles';
const useStyles = makeStyles(theme => ({
container: {
position: 'relative',
backgroundColor: theme.palette.background.l2,
display: 'flex',
flexFlow: 'column',
marginTop: '-50px',
paddingTop: '50px',
width: 50,
boxShadow: `0 ${theme.palette.mode({
light: '3px',
dark: '3px',
})} 5px rgba(0, 0, 0, 0.35)`,
zIndex: 1,
transform: 'translateX(0)',
transition: 'transform .1s ease-out',
'& .zenButton': {
position: 'absolute',
background: theme.palette.background.l2,
top: '50%',
right: '0',
transformOrigin: 'center',
transform: 'translate(100%, -50%) rotate(-90deg) translateY(-30px)',
display: 'none',
},
'&:hover .zenButton': {
display: 'flex',
},
'&.zenMode': {
transform: 'translateX(-100%)',
},
'&.zenMode .zenButton': {
display: 'flex',
},
},
leftPaneIcon: {
'& svg': {
@ -55,7 +80,11 @@ const LeftIconsPane = props => {
props.openDrawerAndSetContent(content);
};
return (
<div className={mStyles.container}>
<div
className={`${mStyles.container} ${
props.isLeftPaneVisible ? '' : 'zenMode'
}`}
>
<Grid
container
spacing={1}
@ -118,6 +147,12 @@ const LeftIconsPane = props => {
</Grid>
</Grid>
<Headway />
{!props.drawer.open && (
<ZenButton
active={!props.isLeftPaneVisible}
onClick={() => props.setLeftPaneVisibility(!props.isLeftPaneVisible)}
/>
)}
</div>
);
};

View file

@ -96,7 +96,7 @@ const useStyles = makeStyles(theme => ({
marginBottom: 0,
},
wilcardsAndMoreLink: {
color: 'white',
color: theme.palette.text.normal,
textDecoration: 'underline',
},
bypassListField: {

View file

@ -7,6 +7,11 @@ import {CAPABILITIES} from '../../constants/devices';
import useCommonStyles from '../useCommonStyles';
import {getDeviceIcon} from '../../utils/iconUtils';
import KebabMenu from '../KebabMenu';
import {
FLIP_ORIENTATION_ALL_DEVICES,
SCREENSHOT_ALL_DEVICES,
} from '../../constants/pubsubEvents';
import pubsub from 'pubsub.js';
function Renderer(props) {
const {device, hidden, transmitNavigatorStatus} = props;
@ -62,6 +67,22 @@ function Renderer(props) {
</div>
</div>
<KebabMenu>
<KebabMenu.Item
onClick={() =>
pubsub.publish(SCREENSHOT_ALL_DEVICES, [{deviceId: device.id}])
}
>
Full Page Screenshot
</KebabMenu.Item>
<KebabMenu.Item
onClick={() =>
pubsub.publish(FLIP_ORIENTATION_ALL_DEVICES, [
{deviceId: device.id},
])
}
>
Tilt Device
</KebabMenu.Item>
<KebabMenu.Item
onClick={props.device.isMuted ? _unmuteDevice : _muteDevice}
>

View file

@ -9,6 +9,7 @@ import ScrollUpIcon from '../icons/ScrollUp';
import ScreenshotIcon from '../icons/FullScreenshot';
import DeviceRotateIcon from '../icons/DeviceRotate';
import InspectElementIcon from '../icons/InspectElement';
import DesignModeIcon from '../icons/DesignMode';
import MutedIcon from '../icons/Muted';
import UnmutedIcon from '../icons/Unmuted';
import useCommonStyles from '../useCommonStyles';
@ -33,6 +34,7 @@ const ScrollControls = ({
toggleInspector,
toggleCSSEditor,
onAllDevicesMutedChange,
onToggleAllDeviceDesignMode,
}) => {
const classes = useStyles();
const theme = useTheme();
@ -44,75 +46,91 @@ const ScrollControls = ({
};
return (
<>
<div className={classes.container}>
<Grid container spacing={1} alignItems="center">
<Grid item className={commonClasses.icon}>
<PrefersColorSchemeSwitch iconProps={iconProps} />
</Grid>
<Grid
item
className={cx(commonClasses.icon, {
[commonClasses.iconSelected]: browser.CSSEditor.isOpen,
})}
onClick={toggleCSSEditor}
>
<Tooltip title="Live CSS Editor">
<div>
<CSSEditor {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={commonClasses.icon}>
<Tooltip title="Take Screenshot">
<div onClick={screenshotAllDevices}>
<ScreenshotIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={commonClasses.icon}>
<Tooltip title="Tilt Devices">
<div onClick={flipOrientationAllDevices}>
<DeviceRotateIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={commonClasses.icon}>
<Tooltip
title={
browser.allDevicesMuted
? 'Unmute all devices'
: 'Mute all devices'
}
>
<div onClick={onAllDevicesMutedChange}>
{browser.allDevicesMuted ? (
<MutedIcon {...iconProps} />
) : (
<UnmutedIcon {...iconProps} />
)}
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonClasses.icon, {
[commonClasses.iconSelected]: browser.isInspecting,
})}
>
<Tooltip title="Inspect Element">
<div onClick={toggleInspector}>
<InspectElementIcon
{...{...iconProps, ...{height: 22, width: 22}}}
/>
</div>
</Tooltip>
</Grid>
<ToggleTouch iconProps={iconProps} />
<ZoomContainer iconProps={iconProps} />
<div className={classes.container}>
<Grid container spacing={1} alignItems="center">
<Grid item className={commonClasses.icon}>
<PrefersColorSchemeSwitch iconProps={iconProps} />
</Grid>
</div>
</>
<Grid
item
className={cx(commonClasses.icon, {
[commonClasses.iconSelected]: browser.CSSEditor.isOpen,
})}
onClick={toggleCSSEditor}
>
<Tooltip title="Live CSS Editor">
<div>
<CSSEditor {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={commonClasses.icon}>
<Tooltip title="Take Screenshot">
<div onClick={screenshotAllDevices}>
<ScreenshotIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={commonClasses.icon}>
<Tooltip title="Tilt Devices">
<div onClick={flipOrientationAllDevices}>
<DeviceRotateIcon {...iconProps} />
</div>
</Tooltip>
</Grid>
<Grid item className={commonClasses.icon}>
<Tooltip
title={
browser.allDevicesMuted
? 'Unmute all devices'
: 'Mute all devices'
}
>
<div onClick={onAllDevicesMutedChange}>
{browser.allDevicesMuted ? (
<MutedIcon {...iconProps} />
) : (
<UnmutedIcon {...iconProps} />
)}
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonClasses.icon, {
[commonClasses.iconSelected]: browser.isInspecting,
})}
>
<Tooltip title="Inspect Element">
<div onClick={toggleInspector}>
<InspectElementIcon
{...{...iconProps, ...{height: 22, width: 22}}}
/>
</div>
</Tooltip>
</Grid>
<Grid
item
className={cx(commonClasses.icon, {
[commonClasses.iconSelected]: browser.allDevicesInDesignMode,
})}
>
<Tooltip
title={
browser.allDevicesInDesignMode
? 'Disable Design Mode on all devices'
: 'Enable Design Mode on all devices'
}
>
<div onClick={onToggleAllDeviceDesignMode}>
<DesignModeIcon {...{...iconProps, ...{height: 22, width: 22}}} />
</div>
</Tooltip>
</Grid>
<ToggleTouch iconProps={iconProps} />
<ZoomContainer iconProps={iconProps} />
</Grid>
</div>
);
};

View file

@ -7,8 +7,6 @@ import {withStyles, withTheme} from '@material-ui/core/styles';
import debounce from 'lodash/debounce';
import pubsub from 'pubsub.js';
import BugIcon from '../icons/Bug';
import MutedIcon from '../icons/Muted';
import UnmutedIcon from '../icons/Unmuted';
import FullScreenshotIcon from '../icons/FullScreenshot';
import ScreenshotIcon from '../icons/Screenshot';
import DeviceRotateIcon from '../icons/DeviceRotate';
@ -30,8 +28,10 @@ import {
OPEN_CONSOLE_FOR_DEVICE,
PROXY_AUTH_ERROR,
APPLY_CSS,
TOGGLE_DEVICE_DESIGN_MODE_STATE,
} from '../../constants/pubsubEvents';
import {CAPABILITIES} from '../../constants/devices';
import {DESIGN_MODE_JS_VALUES} from '../../constants/values';
import styles from './style.module.css';
import {styles as commonStyles} from '../useCommonStyles';
@ -45,9 +45,11 @@ import Maximize from '../icons/Maximize';
import Minimize from '../icons/Minimize';
import Focus from '../icons/Focus';
import Unfocus from '../icons/Unfocus';
import DesignModeIcon from '../icons/DesignMode';
import {captureOnSentry} from '../../utils/logUtils';
import {getBrowserSyncEmbedScriptURL} from '../../services/browserSync';
import Spinner from '../Spinner';
import {isSslValidationFailed} from '../../utils/generalUtils';
const {BrowserWindow} = remote;
@ -141,6 +143,12 @@ class WebView extends Component {
this.subscriptions.push(
pubsub.subscribe(TOGGLE_DEVICE_MUTED_STATE, this.processToggleMuteEvent)
);
this.subscriptions.push(
pubsub.subscribe(
TOGGLE_DEVICE_DESIGN_MODE_STATE,
this.changeDesignModeState
)
);
this.subscriptions.push(
pubsub.subscribe(
@ -201,6 +209,9 @@ class WebView extends Component {
id: this.props.device.id,
loading: false,
});
this.changeDesignModeState({
designMode: !!this.props.device.designMode,
});
});
this.webviewRef.current.addEventListener(
'did-fail-load',
@ -271,6 +282,12 @@ class WebView extends Component {
this._unmuteWebView();
}
}
if (prevProps.device.designMode !== this.props.device.designMode) {
this.changeDesignModeState({
designMode: !!this.props.device.designMode,
});
}
}
getWebContentsId() {
@ -394,10 +411,14 @@ class WebView extends Component {
processScreenshotEvent = async ({
now,
fullScreen = true,
deviceId,
}: {
now?: Date,
fullScreen?: boolean,
}) => {
if (deviceId && this.props.device.id !== deviceId) {
return;
}
this.setState({screenshotInProgress: true});
try {
await this.closeBrowserSyncSocket(this.webviewRef.current);
@ -421,8 +442,9 @@ class WebView extends Component {
this.setState({screenshotInProgress: false});
};
processFlipOrientationEvent = () => {
if (!this.isMobile) {
processFlipOrientationEvent = (message = {}) => {
const {deviceId} = message;
if (deviceId && this.props.device.id !== deviceId) {
return;
}
this._flipOrientation();
@ -432,6 +454,16 @@ class WebView extends Component {
this.getWebContents().setAudioMuted(muted);
};
changeDesignModeState = ({designMode}) => {
this.webviewRef.current
.executeJavaScript(
`document.designMode = "${
designMode ? DESIGN_MODE_JS_VALUES.ON : DESIGN_MODE_JS_VALUES.OFF
}";`
)
.catch(captureOnSentry);
};
processOpenDevToolsInspectorEvent = message => {
const {
x: webViewX,
@ -667,6 +699,11 @@ class WebView extends Component {
});
};
_toggleDesignMode = () => {
const {id: deviceId} = this.props.device;
this.props.onToggleDeviceDesignMode(deviceId);
};
_focusDevice = () => {
this.props.setPreviewLayout(INDIVIDUAL_LAYOUT);
this.props.setFocusedDevice(this.props.device.id);
@ -935,27 +972,6 @@ class WebView extends Component {
<ScreenshotIcon height={18} />
</div>
</Tooltip>
<Tooltip title="Full Page Screenshot">
<div
className={cx(styles.webViewToolbarIcons, classes.icon)}
onClick={this.processScreenshotEvent}
>
<FullScreenshotIcon height={18} />
</div>
</Tooltip>
{this.isMobile ? (
<Tooltip title="Tilt Device">
<div
className={cx(styles.webViewToolbarIcons, classes.icon, {
[classes.iconSelected]: this.state.isTilted,
})}
onClick={this._flipOrientation}
>
<DeviceRotateIcon height={17} />
</div>
</Tooltip>
) : null}
<Tooltip title="Disable event mirroring">
<div
className={cx(styles.webViewToolbarIcons, classes.icon, {
@ -966,6 +982,20 @@ class WebView extends Component {
<UnplugIcon height={30} />
</div>
</Tooltip>
<Tooltip
title={`${
this.props.device.designMode ? 'Disable' : 'Enable'
} Design Mode`}
>
<div
className={cx(styles.webViewToolbarIcons, classes.icon, {
[classes.iconSelected]: this.props.device.designMode,
})}
onClick={this._toggleDesignMode}
>
<DesignModeIcon height={20} />
</div>
</Tooltip>
</div>
<div className={cx(styles.webViewToolbarRight)}>
<Tooltip
@ -1008,9 +1038,17 @@ class WebView extends Component {
>
<p>ERROR: {errorCode}</p>
<p className={cx(styles.errorDesc)}>{errorDesc}</p>
{proxyAuthError && (
<p className={cx(styles.errorDesc)}>Proxy Authentication Error</p>
)}
{isSslValidationFailed(errorCode) && (
<p className={cx(classes.errorHelpSuggestion)}>
If you wish to proceed, you can disable the SSL validation in
the user preferences.
</p>
)}
</div>
{this._getWebViewTag(deviceStyles, containerWidth, containerHeight)}
</div>
@ -1061,5 +1099,12 @@ const webViewStyles = theme => ({
bottom: 0,
},
},
errorHelpSuggestion: {
position: 'absolute',
top: '25%',
width: '100%',
padding: 35,
background: theme.palette.primary.main,
},
});
export default withStyles(webViewStyles)(withTheme(WebView));

View file

@ -53,6 +53,7 @@
display: none;
z-index: 1;
text-align: center;
color: #f8f8f8;
}
.deviceOverlay.overlayEnabled {
@ -62,6 +63,7 @@
.errorDesc {
font-size: 20px;
word-break: break-all;
}
.iconWrapperS {

View file

@ -0,0 +1,47 @@
// @flow
import React from 'react';
import Tooltip from '@material-ui/core/Tooltip';
import {useTheme, makeStyles} from '@material-ui/core/styles';
import Chevron from '../icons/Chevron';
import cx from 'classnames';
/**
* Button with a Chevron in the middle used for toggling zen mode on/off.
* @param active Indicates whether zen mode is on or not.
* @param onClick Callback function for when the button is clicked.
*/
const ZenButton = ({active = false, onClick}) => {
const classes = useStyles();
const theme = useTheme();
return (
<div className={cx(['zenButton', classes.container])} onClick={onClick}>
<Tooltip title="Hide/Show">
<div className={cx([classes.icon, {invert: active}])}>
<Chevron width={19} height={8} color={theme.palette.lightIcon.main} />
</div>
</Tooltip>
</div>
);
};
const useStyles = makeStyles(theme => ({
container: {
alignItems: 'center',
borderRadius: '0 0 8px 8px',
display: 'flex',
height: '20px',
justifyContent: 'center',
textAlign: 'center',
width: '80px',
cursor: 'pointer',
boxShadow: '0 5px 5px rgba(0, 0, 0, 0.35)',
},
icon: {
marginTop: '-5px',
'&.invert': {
transform: 'rotateX(180deg) translateY(-5px)',
},
},
}));
export default ZenButton;

View file

@ -16,6 +16,7 @@ import styles from './styles.module.css';
import useCommonStyles from '../useCommonStyles';
import './otherStyles.css';
import {Tooltip} from '@material-ui/core';
import {MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL} from '../../constants';
function BrowserZoom(props) {
const [showExpanded, setShowExpanded] = useState(false);
@ -51,7 +52,7 @@ function BrowserZoom(props) {
}
};
const value = Math.round(props.browser.zoomLevel * 100);
const zoomLevel = props.browser.zoomLevel;
return (
<div
@ -69,7 +70,11 @@ function BrowserZoom(props) {
})}
>
<ToggleButtonGroup value={[]} onChange={_zoomChange}>
<ToggleButton value="zoomOut" disabled={value === 20} disableRipple>
<ToggleButton
value="zoomOut"
disabled={zoomLevel === MIN_ZOOM_LEVEL}
disableRipple
>
&ndash;
</ToggleButton>
<Typography
@ -79,9 +84,13 @@ function BrowserZoom(props) {
'MuiToggleButton-root'
)}
>
{value}%
{Math.round(props.browser.zoomLevel * 100)}%
</Typography>
<ToggleButton value="zoomIn" disabled={value === 200} disableRipple>
<ToggleButton
value="zoomIn"
disabled={zoomLevel === MAX_ZOOM_LEVEL}
disableRipple
>
+
</ToggleButton>
</ToggleButtonGroup>

View file

@ -0,0 +1,22 @@
import React from 'react';
/**
* Flattened Chevron icon.
*/
export default ({width, height, color, padding, margin}) => (
<svg
height={height}
width={width}
style={{padding, margin}}
viewBox={`0 0 ${width} ${height}`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
>
<path
d="M1 6.5L9.5 2L18 6.5"
stroke={color}
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
);

View file

@ -0,0 +1,30 @@
import React, {Fragment} from 'react';
export default ({width, height, color, padding, margin}) => (
<Fragment>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
version="1.1"
x="0px"
y="0px"
height={height}
width={width}
fill={color}
style={{padding, margin}}
className="designModeIcon"
viewBox="0 0 36 36"
>
<path
d="M28 30H6V8h13.22l2-2H6a2 2 0 0 0-2 2v22a2 2 0 0 0 2 2h22a2 2 0 0 0 2-2V15l-2 2z"
className="clr-i-outline clr-i-outline-path-1"
fill="currentColor"
/>
<path
d="M33.53 5.84l-3.37-3.37a1.61 1.61 0 0 0-2.28 0L14.17 16.26l-1.11 4.81A1.61 1.61 0 0 0 14.63 23a1.69 1.69 0 0 0 .37 0l4.85-1.07L33.53 8.12a1.61 1.61 0 0 0 0-2.28zM18.81 20.08l-3.66.81l.85-3.63L26.32 6.87l2.82 2.82zM30.27 8.56l-2.82-2.82L29 4.16L31.84 7z"
className="clr-i-outline clr-i-outline-path-2"
fill="currentColor"
/>
</svg>
</Fragment>
);

View file

@ -31,6 +31,9 @@ const lightTheme = {
l10: '#d2d2d2',
l20: '#8a8a8a',
},
border: {
color: '#000000',
},
header: {
main: '#F5F5F5',
},
@ -72,6 +75,9 @@ const darkTheme = {
l10: '#9e9e9e',
l20: '#aeaeae',
},
border: {
color: '#ffffff',
},
header: {
main: '#252526',
},

View file

@ -49,6 +49,7 @@ export type Device = {
type: DeviceType,
source: Source,
isMuted: boolean,
designMode: boolean,
};
function getOS(device) {

View file

@ -0,0 +1,16 @@
// @flow
/**
* Default zoom level for the application.
*/
export const DEFAULT_ZOOM_LEVEL = 0.6;
/**
* Minimum zoom threshold for the application.
*/
export const MIN_ZOOM_LEVEL = 0.2;
/**
* Maximum zoom threshold for the application.
*/
export const MAX_ZOOM_LEVEL = 2.0;

View file

@ -24,3 +24,6 @@ export const HIDE_PERMISSION_POPUP_DUE_TO_RELOAD =
'HIDE_PERMISSION_POPUP_DUE_TO_RELOAD';
export const PERMISSION_MANAGEMENT_PREFERENCE_CHANGED =
'PERMISSION_MANAGEMENT_PREFERENCE_CHANGED';
export const TOGGLE_DEVICE_DESIGN_MODE_STATE =
'TOGGLE_DEVICE_DESIGN_MODE_STATE';

View file

@ -2,3 +2,13 @@ export const SCREENSHOT_MECHANISM = {
V1: 'V1',
V2: 'V2',
};
export const SSL_ERROR_CODES = {
FIRST: -200,
LAST: -299,
};
export const DESIGN_MODE_JS_VALUES = {
ON: 'on',
OFF: 'off',
};

View file

@ -3,18 +3,20 @@ import React, {Fragment} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Grid from '@material-ui/core/Grid';
import Header from '../../components/Header';
import DevicePreviewerContainer from '../DevicePreviewerContainer';
import DrawerContainer from '../DrawerContainer';
import * as BrowserActions from '../../actions/browser';
import {DEVTOOLS_MODES} from '../../constants/previewerLayouts';
import LeftIconsPaneContainer from '../LeftIconsPaneContainer';
type Props = {};
import HeaderContainer from '../HeaderContainer';
import os from 'os';
import HorizontalSpacer from '../../components/HorizontalSpacer';
import AppNotification from '../../components/AppNotification/AppNotification';
const Browser = ({browser}) => (
<Fragment>
<Header />
{os.platform() === 'darwin' && <HorizontalSpacer />}
<HeaderContainer />
<div style={{display: 'flex', height: '100%'}}>
<LeftIconsPaneContainer />
<div
@ -60,6 +62,7 @@ const Browser = ({browser}) => (
) : null}
</div>
</div>
<AppNotification />
</Fragment>
);

View file

@ -0,0 +1,19 @@
// @flow
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import Header from '../../components/Header';
import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
isHeaderVisible: state.browser.isHeaderVisible,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(BrowserActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);

View file

@ -1,5 +1,4 @@
// @flow
import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
@ -9,6 +8,7 @@ import * as BrowserActions from '../../actions/browser';
function mapStateToProps(state) {
return {
drawer: state.browser.drawer,
isLeftPaneVisible: state.browser.isLeftPaneVisible,
};
}

View file

@ -1,11 +1,10 @@
import React, {Component} from 'react';
import {Provider} from 'react-redux';
import {ConnectedRouter} from 'connected-react-router';
import log from 'electron-log';
import {makeStyles} from '@material-ui/core/styles';
import {ThemeProvider} from '@material-ui/styles';
import {remote} from 'electron';
import Routes from '../Routes';
import AppContent from '../AppContent';
import ErrorBoundary from '../components/ErrorBoundary';
import {
registerShortcut,
@ -29,22 +28,19 @@ import {toggleBookmarkUrl} from '../actions/bookmarks';
import pubsub from 'pubsub.js';
import {PROXY_AUTH_ERROR} from '../constants/pubsubEvents';
import useCreateTheme from '../components/useCreateTheme';
import {DEFAULT_ZOOM_LEVEL} from '../constants';
function App({history}) {
function App() {
const theme = useCreateTheme();
return (
<ThemeProvider theme={theme}>
{process.env.NODE_ENV !== 'development' ? (
<ErrorBoundary>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
<AppContent />
</ErrorBoundary>
) : (
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
<AppContent />
)}
</ThemeProvider>
);
@ -95,7 +91,7 @@ export default class Root extends Component {
registerShortcut(
{id: 'ZoomReset', title: 'Zoom Reset', accelerators: ['mod+0']},
() => {
store.dispatch(onZoomChange(0.6));
store.dispatch(onZoomChange(DEFAULT_ZOOM_LEVEL));
},
true
);
@ -226,10 +222,10 @@ export default class Root extends Component {
};
render() {
const {store, history} = this.props;
const {store} = this.props;
return (
<Provider store={store}>
<App history={history} />
<App />
</Provider>
);
}

View file

@ -4,9 +4,10 @@ const {promisify} = require('util');
const Jimp = require('jimp');
const os = require('os');
const path = require('path');
const UUID = require('uuid/v4');
const uuid = require('uuid');
const fs = require('fs-extra');
const UUID = uuid.v4;
const tempDir = path.join(os.tmpdir(), UUID());
registerPromiseWorker(({images, direction, resultFilename}) => {

View file

@ -6,7 +6,7 @@ import {remote} from 'electron';
import {render} from 'react-dom';
import {AppContainer} from 'react-hot-loader';
import Root from './containers/Root';
import {configureStore, history} from './store/configureStore';
import {configureStore} from './store/configureStore';
import './app.global.css';
import * as Sentry from '@sentry/electron';
import appMetadata from './services/db/appMetadata';
@ -40,7 +40,7 @@ const store = configureStore();
render(
<AppContainer>
<Root store={store} history={history} />
<Root store={store} />
</AppContainer>,
document.getElementById('root')
);
@ -51,7 +51,7 @@ if (module.hot) {
const NextRoot = require('./containers/Root').default;
render(
<AppContainer>
<NextRoot store={store} history={history} />
<NextRoot store={store} />
</AppContainer>,
document.getElementById('root')
);

View file

@ -63,6 +63,19 @@ migrateDeviceSchema();
if (process.env.NODE_ENV !== 'development') {
Sentry.init({
dsn: 'https://f2cdbc6a88aa4a068a738d4e4cfd3e12@sentry.io/1553155',
environment: process.env.NODE_ENV,
beforeSend: (event, hint) => {
// Suppress address already in use error
if (
(event?.exception?.values?.[0]?.value || '').indexOf(
'listen EADDRINUSE: address already in use'
) > -1
) {
return null;
}
event.tags = {appVersion: app.getVersion()};
return event;
},
});
}
@ -355,6 +368,7 @@ const createWindow = async () => {
} else {
mainWindow.show();
}
mainWindow.maximize();
onResize();
});

View file

@ -26,6 +26,10 @@ import {
NEW_CSS_EDITOR_STATUS,
NEW_CSS_EDITOR_POSITION,
NEW_CSS_EDITOR_CONTENT,
TOGGLE_ALL_DEVICES_DESIGN_MODE,
TOGGLE_DEVICE_DESIGN_MODE,
SET_HEADER_VISIBILITY,
SET_LEFT_PANE_VISIBILITY,
} from '../actions/browser';
import {
CHANGE_ACTIVE_THROTTLING_PROFILE,
@ -56,6 +60,8 @@ import {
saveLastOpenedAddress,
} from '../utils/navigatorUtils';
import {updateExistingUrl} from '../services/searchUrlSuggestions';
import {normalizeZoomLevel} from '../utils/browserUtils';
import {DEFAULT_ZOOM_LEVEL} from '../constants';
export const FILTER_FIELDS = {
OS: 'OS',
@ -188,6 +194,9 @@ export type BrowserStateType = {
windowSize: WindowSizeType,
allDevicesMuted: boolean,
networkConfiguration: NetworkConfigurationType,
allDevicesInDesignMode: boolean,
isHeaderVisible: boolean,
isLeftPaneVisible: boolean,
};
let _activeDevices = null;
@ -220,13 +229,14 @@ function _getActiveDevices() {
activeDevices.forEach(device => {
device.loading = false;
device.isMuted = false;
device.designMode = false;
});
}
return activeDevices;
}
function _getUserPreferences(): UserPreferenceType {
return settings.get(USER_PREFERENCES);
return settings.get(USER_PREFERENCES) || {};
}
function _setUserPreferences(userPreferences) {
@ -306,7 +316,8 @@ export default function browser(
? getLastOpenedAddress()
: getHomepage(),
currentPageMeta: {},
zoomLevel: _getUserPreferences().zoomLevel || 0.6,
zoomLevel:
normalizeZoomLevel(_getUserPreferences().zoomLevel) || DEFAULT_ZOOM_LEVEL,
theme: _getUserPreferences().theme,
previousZoomLevel: null,
scrollPosition: {x: 0, y: 0},
@ -341,6 +352,9 @@ export default function browser(
windowSize: getWindowSize(),
allDevicesMuted: false,
networkConfiguration: _getNetworkConfiguration(),
allDevicesInDesignMode: false,
isHeaderVisible: true,
isLeftPaneVisible: true,
},
action: Action
) {
@ -538,6 +552,42 @@ export default function browser(
proxy: action.profile,
},
};
case TOGGLE_ALL_DEVICES_DESIGN_MODE:
const nextDevices = state.devices;
const nextDesginModeForAll = !state.allDevicesInDesignMode;
nextDevices.forEach(d => (d.designMode = nextDesginModeForAll));
return {
...state,
allDevicesInDesignMode: nextDesginModeForAll,
devices: nextDevices,
};
case TOGGLE_DEVICE_DESIGN_MODE:
const deviceIndex = state.devices.findIndex(
x => x.id === action.deviceId
);
if (deviceIndex === -1) return {...state};
const nextDesignModeForDevice = !state.devices[deviceIndex].designMode;
state.devices[deviceIndex] = {
...state.devices[deviceIndex],
designMode: nextDesignModeForDevice,
};
return {
...state,
allDevicesInDesignMode: state.devices.every(x => x.designMode),
devices: [...state.devices],
};
case SET_HEADER_VISIBILITY:
return {
...state,
isHeaderVisible: action.isVisible,
};
case SET_LEFT_PANE_VISIBILITY:
return {
...state,
isLeftPaneVisible: action.isVisible,
};
default:
return state;
}

View file

@ -1,13 +1,11 @@
// @flow
import {combineReducers} from 'redux';
import {connectRouter} from 'connected-react-router';
import browser from './browser';
import bookmarks from './bookmarks';
import statusBar from './statusBar';
export default function createRootReducer(history: History) {
export default function createRootReducer() {
return combineReducers({
router: connectRouter(history),
browser,
bookmarks,
statusBar,

View file

@ -1,13 +1,9 @@
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {createHashHistory} from 'history';
import {routerMiddleware, routerActions} from 'connected-react-router';
import {createLogger} from 'redux-logger';
import createRootReducer from '../reducers';
const history = createHashHistory();
const rootReducer = createRootReducer(history);
const rootReducer = createRootReducer();
const configureStore = initialState => {
// Redux Configuration
@ -28,14 +24,9 @@ const configureStore = initialState => {
middleware.push(logger);
}
// Router Middleware
const router = routerMiddleware(history);
middleware.push(router);
// Redux DevTools Configuration
const actionCreators = {
...routerActions,
};
const actionCreators = {};
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
@ -64,4 +55,4 @@ const configureStore = initialState => {
return store;
};
export default {configureStore, history};
export default {configureStore};

View file

@ -8,5 +8,3 @@ const selectedConfigureStore =
: configureStoreDev;
export const {configureStore} = selectedConfigureStore;
export const {history} = selectedConfigureStore;

View file

@ -1,13 +1,9 @@
// @flow
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {createHashHistory} from 'history';
import {routerMiddleware} from 'connected-react-router';
import createRootReducer from '../reducers';
const history = createHashHistory();
const rootReducer = createRootReducer(history);
const router = routerMiddleware(history);
const rootReducer = createRootReducer();
const heap = () => next => action => {
window.requestIdleCallback(() => {
if (window.heap) {
@ -19,10 +15,10 @@ const heap = () => next => action => {
});
return next(action);
};
const enhancer = applyMiddleware(thunk, router, heap);
const enhancer = applyMiddleware(thunk, heap);
function configureStore(initialState) {
return createStore(rootReducer, initialState, enhancer);
}
export default {configureStore, history};
export default {configureStore};

View file

@ -11,7 +11,7 @@ const useStyles = makeStyles(theme => ({
height: '40vh',
marginBottom: '10px',
marginTop: '10px',
border: '1px white solid',
border: `1px solid ${theme.palette.border.color}`,
padding: '0 10px',
borderRadius: '4px',
},

View file

@ -0,0 +1,19 @@
// @flow
import {MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL} from '../constants';
/**
* Ensures that the given zoom level stays between MIN_ZOOM_LEVEL and MAX_ZOOM_LEVEL and returns it.
* @param zoomLevel The zoom level to be normalized.
*/
export function normalizeZoomLevel(zoomLevel: number): number {
if (zoomLevel < MIN_ZOOM_LEVEL) {
return MIN_ZOOM_LEVEL;
}
if (zoomLevel > MAX_ZOOM_LEVEL) {
return MAX_ZOOM_LEVEL;
}
return zoomLevel;
}

View file

@ -0,0 +1,18 @@
import os from 'os';
export const MIN_NUMBER_OF_DEVICES = 2;
export function getRecommendedMaxNumberOfDevices() {
const logicalCpuInfos = os.cpus();
const cpuSpeed = logicalCpuInfos[0].speed;
const cpuCount = logicalCpuInfos.length;
const value = Math.max(
MIN_NUMBER_OF_DEVICES,
Math.trunc(cpuCount * (cpuSpeed / 2000))
);
return value;
}
export const recommendedMaxNumberOfDevices = getRecommendedMaxNumberOfDevices();

View file

@ -2,6 +2,7 @@ import {app} from 'electron';
import path from 'path';
import fs from 'fs';
import os from 'os';
import {SSL_ERROR_CODES} from '../constants/values';
export const getPackageJson = () => {
let appPath;
@ -36,3 +37,9 @@ export const getEnvironmentInfo = () => {
osInfo,
};
};
export function isSslValidationFailed(errorCode) {
return (
errorCode <= SSL_ERROR_CODES.FIRST && errorCode >= SSL_ERROR_CODES.LAST
);
}

View file

@ -1,7 +1,7 @@
{
"name": "Responsively-App",
"productName": "ResponsivelyApp",
"version": "0.13.0",
"version": "0.14.1",
"description": "A developer-friendly browser for developing responsive web apps",
"scripts": {
"build": "concurrently \"yarn build-main\" \"yarn build-renderer\"",
@ -225,12 +225,11 @@
"babel-plugin-transform-react-remove-prop-types": "^0.4.20",
"chalk": "^2.4.1",
"concurrently": "^5.2.0",
"connected-react-router": "^6.5.2",
"cross-env": "^5.2.0",
"cross-spawn": "^6.0.5",
"css-loader": "^1.0.1",
"detect-port": "^1.3.0",
"electron": "^9.1.1",
"electron": "^9.3.1",
"electron-builder": "^22.8.0",
"electron-devtools-installer": "^3.1.1",
"enzyme": "^3.7.0",
@ -306,7 +305,6 @@
"electron-updater": "^4.3.1",
"electron-util": "^0.14.2",
"framer-motion": "^2.2.0",
"history": "^4.7.2",
"jimp": "^0.12.1",
"lodash": "^4.17.19",
"merge-img": "^2.1.3",
@ -324,8 +322,6 @@
"react-redux": "^7.1.0",
"react-resizable": "^1.10.1",
"react-rnd": "^10.2.2",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-select": "^3.1.0",
"react-switch": "^5.0.1",
"react-tabs": "^3.0.0",

View file

@ -66,7 +66,7 @@ Check it out our GitHub repo - [![GitHub stars](https://img.shields.io/github/st
Follow on Twitter for future updates - [![Twitter Follow](https://img.shields.io/twitter/follow/ResponsivelyApp?style=social)](https://twitter.com/ResponsivelyApp)
Sponsor this project on [Open Collective](opencollective.com/responsively)
Sponsor this project on [Open Collective](https://opencollective.com/responsively)
Come say hi to us on [Slack](https://join.slack.com/t/responsively/shared_invite/zt-haoieftz-IsMw64H6jXC23pJ16ROLzw)!
</description>

View file

@ -3,26 +3,10 @@ const {version, build, productName} = require('../package.json');
const path = require('path');
const fs = require('fs');
const RELATIVE_FOLDER_PATH = '../release';
const getZipFile = () => {
const product = (build || {}).productName || productName;
const zipName = `${product}-${version}.zip`;
const zipPath = path.resolve(__dirname, RELATIVE_FOLDER_PATH, zipName);
if (fs.existsSync(zipPath)) {
console.log(`\nIncluded '${zipName}' in publish files`);
return zipPath;
}
throw new Error(`Expected zip file '${zipPath}' not found`);
};
const getExtraPublishFiles = () =>
generateChecksums().then(files => {
const zipFile = getZipFile();
const all = [zipFile, ...files];
console.log(`\nExtra Files Included:\n${all.join('\n')}`);
return all;
console.log(`\nExtra Files Included:\n${files.join('\n')}`);
return files;
});
exports.default = getExtraPublishFiles;

View file

@ -1,6 +1,17 @@
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const pkg = require('../package.json');
const version = pkg.version;
const requiredFiles = [
`Responsively-App-${version}.x86_64.rpm`,
`ResponsivelyApp-${version}-mac.zip`,
`ResponsivelyApp-${version}.AppImage`,
`ResponsivelyApp-${version}.dmg`,
`ResponsivelyApp ${version}.exe`,
`ResponsivelyApp Setup ${version}.exe`,
];
function hashFile(file, algorithm = 'sha512', encoding = 'hex', options = {}) {
return new Promise((resolve, reject) => {
@ -31,17 +42,16 @@ const SKIP_SUFFIX_LIST = [CHECKSUM_SUFFIX, '.yml', '.yaml', '.txt'];
const generateChecksums = async () => {
const result = [];
const installerPath = path.resolve(__dirname, RELATIVE_FOLDER_PATH);
const files = await fs.promises.readdir(installerPath);
console.log("\nGenerating checksum files for files in: '%s'", installerPath);
for (const file of files) {
for (const file of requiredFiles) {
if (SKIP_SUFFIX_LIST.some(s => file.endsWith(s))) continue;
const filePath = path.join(installerPath, file);
const stat = await fs.promises.stat(filePath);
if (stat.isFile()) {
const checksumFile = `${file}${CHECKSUM_SUFFIX}`;
const checksumFile = `${file.replace(/ /g, '-')}${CHECKSUM_SUFFIX}`;
const checksumFilePath = path.join(installerPath, checksumFile);
const checksum = await hashFile(filePath);
await fs.promises.writeFile(checksumFilePath, checksum);

View file

@ -5193,13 +5193,6 @@ connect@3.6.6:
parseurl "~1.3.2"
utils-merge "1.0.1"
connected-react-router@^6.5.2:
version "6.8.0"
resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.8.0.tgz#ddc687b31d498322445d235d660798489fa56cae"
integrity sha512-E64/6krdJM3Ag3MMmh2nKPtMbH15s3JQDuaYJvOVXzu6MbHbDyIvuwLOyhQIuP4Om9zqEfZYiVyflROibSsONg==
dependencies:
prop-types "^15.7.2"
console-browserify@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
@ -6358,10 +6351,10 @@ electron-util@^0.14.2:
electron-is-dev "^1.1.0"
new-github-issue-url "^0.2.1"
electron@^9.1.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/electron/-/electron-9.1.1.tgz#d52c9873be4113287c3eb2b02f85bad6644b100e"
integrity sha512-BYvroBLV9x7G4iN33P/IxeZqwjl62/9VuBAF1CoM0m6OeheaiLog1ZMKLlCqVXycJvvrAvLHc454DDEmwnqqhA==
electron@^9.3.1:
version "9.3.1"
resolved "https://registry.yarnpkg.com/electron/-/electron-9.3.1.tgz#e301932c5c0537d8c9a8850d216d3ba454dbf55c"
integrity sha512-DScrhqBT4a54KfdF0EoipALpHmdQTn3m7SSCtbpTcEcG+UDUiXad2cOfW6DHeVH7N+CVDKDG12q2PhVJjXkFAA==
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^12.0.12"
@ -8372,18 +8365,6 @@ highlight-es@^1.0.0:
is-es2016-keyword "^1.0.0"
js-tokens "^3.0.0"
history@^4.7.2, history@^4.9.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -8393,7 +8374,7 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -10600,7 +10581,7 @@ longest-streak@^2.0.1:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"
integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@ -11000,14 +10981,6 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
mini-create-react-context@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040"
integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==
dependencies:
"@babel/runtime" "^7.5.5"
tiny-warning "^1.0.3"
mini-css-extract-plugin@^0.4.4:
version "0.4.5"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.5.tgz#c99e9e78d54f3fa775633aee5933aeaa4e80719a"
@ -13157,7 +13130,7 @@ react-input-autosize@^2.2.2:
dependencies:
prop-types "^15.5.8"
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0:
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -13202,35 +13175,6 @@ react-rnd@^10.2.2:
react-draggable "4.4.3"
tslib "2.0.0"
react-router-dom@^5.0.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0, react-router@^5.0.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-select@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"
@ -13827,11 +13771,6 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@ -15618,12 +15557,12 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tiny-invariant@^1.0.2, tiny-invariant@^1.0.4, tiny-invariant@^1.0.6:
tiny-invariant@^1.0.4, tiny-invariant@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3:
tiny-warning@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
@ -16316,11 +16255,6 @@ validator@^13.1.1:
resolved "https://registry.yarnpkg.com/validator/-/validator-13.1.1.tgz#f8811368473d2173a9d8611572b58c5783f223bf"
integrity sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw==
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"

3
website/.gitignore vendored
View file

@ -1,3 +0,0 @@
dist
node_modules
.DS_Store

View file

@ -1,7 +0,0 @@
# LICENSE #
Leap Bootstrap Theme by Medium Rare
Use of this theme is subject to the license terms set out on the Bootstrap Themes site:
https://themes.getbootstrap.com/licenses/

View file

@ -1,30 +0,0 @@
# README #
Leap Bootstrap Theme by Medium Rare
### Where are the docs? ###
* Formal documentation is located at http://leap.mediumra.re/documentation/index.html - accessible from the **Documentation** link on most demo pages.
* You can find lists of the styled components at pages/components-leap.html and pages/components-bootstrap.html
### Getting Set Up (optional) ###
Setup instructions are located in the docs mentioned above.
The short version:
* npm install -g gulp-cli
* npm install
* gulp
### Getting Support ###
Medium Rare provides support for bugfixes and guidance on using the theme.
To access support, find the support link in your Bootstrap Marketplace dashboard.
### Giving Feedback ###
We strive to improve our products and we rely on feedback from our customers.
Please feel free to share any feedback about Leap via twitter @mrareweb or feedback(at)mrare.co.

View file

@ -1,136 +0,0 @@
-
files: "clipboard.min.js"
from: "node_modules/clipboard/dist"
to: "pages/assets/js"
-
files: "jquery.min.js"
from: "node_modules/jquery/dist"
to: "pages/assets/js"
-
files: "jquery.countdown.min.js"
from: "node_modules/jquery-countdown/dist"
to: "pages/assets/js"
-
files: "flatpickr.min.js"
from: "node_modules/flatpickr/dist"
to: "pages/assets/js"
-
files: "flatpickr.min.css"
from: "node_modules/flatpickr/dist"
to: "scss/custom/components/plugins"
-
files: "flickity.pkgd.min.js"
from: "node_modules/flickity/dist"
to: "pages/assets/js"
-
files: "flickity.css"
from: "node_modules/flickity/dist"
to: "scss/custom/components/plugins"
-
files: "ion.rangeSlider.min.js"
from: "node_modules/ion-rangeslider/js"
to: "pages/assets/js"
-
files: "ion.rangeSlider.css"
from: "node_modules/ion-rangeslider/css"
to: "scss/custom/components/plugins"
-
files: "isotope.pkgd.min.js"
from: "node_modules/isotope-layout/dist"
to: "pages/assets/js"
-
files: "jquery.fancybox.min.css"
from: "node_modules/@fancyapps/fancybox/dist"
to: "scss/custom/components/plugins"
-
files: "jquery.fancybox.min.js"
from: "node_modules/@fancyapps/fancybox/dist"
to: "pages/assets/js"
-
files: "popper.min.js"
from: "node_modules/popper.js/dist/umd"
to: "pages/assets/js"
-
files: "popper.min.js.map"
from: "node_modules/popper.js/dist/umd"
to: "pages/assets/js"
-
files: "prism.js"
from: "node_modules/prismjs"
to: "pages/assets/js"
-
files: "prism.css"
from: "node_modules/prismjs/themes"
to: "scss/custom/components/plugins"
-
files: "prism-okaidia.css"
from: "node_modules/prismjs/themes"
to: "scss/custom/components/plugins"
-
files:
- "scrollMonitor.js"
- "scrollMonitor.js.map"
from: "node_modules/scrollmonitor"
to: "pages/assets/js"
-
files: "smooth-scroll.polyfills.min.js"
from: "node_modules/smooth-scroll/dist"
to: "pages/assets/js"
-
files:
- "svg-injector.umd.production.js"
- "svg-injector.umd.production.js.map"
from: "node_modules/@tanem/svg-injector/dist"
to: "pages/assets/js"
-
files:
- "typed.min.js"
- "typed.min.js.map"
from: "node_modules/typed.js/lib"
to: "pages/assets/js"
-
files: "aos.css"
from: "node_modules/aos/dist"
to: "scss/custom/components/plugins"
-
files: "aos.js"
from: "node_modules/aos/dist"
to: "pages/assets/js"
-
files: "twitterFetcher_min.js"
from: "node_modules/twitter-fetcher/js"
to: "pages/assets/js"
-
files:
- "jarallax.min.js"
- "jarallax-video.min.js"
- "jarallax-element.min.js"
- "jarallax.min.js.map"
- "jarallax-video.min.js.map"
- "jarallax-element.min.js.map"
from: "node_modules/jarallax/dist"
to: "pages/assets/js"
-
files: "jarallax.css"
from: "node_modules/jarallax/dist"
to: "scss/custom/components/plugins"
-
files: "plyr.css"
from: "node_modules/plyr/dist"
to: "scss/custom/components/plugins"
-
files:
- "plyr.polyfilled.min.js"
- "plyr.polyfilled.min.js.map"
from: "node_modules/plyr/dist"
to: "pages/assets/js"
-
files:
- "*.woff"
- "*.woff2"
from: "node_modules/inter-ui/Inter UI (web)"
to: "assets/fonts"
-
files: "jquery.smartWizard.min.js"
from: "node_modules/smartwizard/dist/js"
to: "pages/assets/js"

View file

@ -1,45 +0,0 @@
<?php
// SETTINGS FOR MAILCHIMP SUBSCRIPTION
// Log in to MailChimp and create an API key under:
// [ Account ] -> [ Extras ] -> [ API Keys ]
$apiKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us8';
// Find your list ID by opening the list in MailChimp, then:
// [ Settings ] -> [ List name and defaults ]
$listId = 'abcdefghij';
// Form data to use as email field
// (The name="..." value from the email field in your HTML form)
$emailField = 'email';
// Fields to be submitted to your MailChimp list along with the email address
// In this example, "NAME" is the field in your MailChimp list,
// and $_POST["name"] is the form data from the user to fill that field
$mergeFields = array(
"NAME" => $_POST["name"],
);
// What the user's status will be after submitting.
// Options are 'pending', 'subscribed', 'unsubscribed', 'cleaned'
// We recommend 'pending' as this will result in the user receiving
// an opt-in confirmation email from MailChimp.
// For single opt-in use 'subscribed'.
$status = 'pending';
// Text to show user upon successful subscribe operation
$successMessage = "Thanks for subscribing, please check your inbox for confirmation.";
// Text to show when the user is already subscribed to the list
$alreadySubscribed = "You are already subscribed to this list.";
// Text to show when the user is already subscribed to the list
$checkConfirmation = "Your subscription is pending, check your inbox for a confirmation link.";
// Google reCAPTCHA
// If your form is configured with a reCAPTCHA widget, this secret key will be used to validate with Google's server.
$recaptchaSecretKey = 'insert-your-recaptcha-secret-key-here';
$recaptchaErrorMessage = 'There was a problem verifying the Google reCaptcha. Please try again.';
require('vendor/mediumrare/mailchimp_subscribe.php');
?>

View file

@ -1,75 +0,0 @@
<?php
// error_reporting(-1);
// ini_set('display_errors', 'On');
/*----------------------------------------------------------------------------*\
|* Email settings for sending all emails from your website forms. *|
\*============================================================================*/
// Set the details of your SMTP server here:
// These details will be used to log in to the SMTP server and send an email
// to the site admin when a user submits a form.
// Optionally, you can send an email to the user to confirm receipt of their form submission.
// Outgoing Server Settings - replace values on the right of the = sign with your own.
// These 3 settings are are required.
// We do not recommend using Gmail as a server to send email.
// We recommend that you set up an email address on your hosting provider to perform the sending.
// (see cPanel -> email accounts). Using an account at your host improves deliverability.
// These are the Outgoing Server (SMTP) details provided by your email host
$outgoingServerAddress = 'server.company.com'; // consult your hosting provider.
$outgoingServerPort = '25'; // '587' , '25' - consult your hosting provider
$outgoingServerSecurity = 'tls'; // 'ssl' , 'tls' , null - consult your hosting provider.
// Sending Account Settings - replace these details with an email account held on the SMTP server entered above.
// This will also be used as the account to send the confirmation to the user.
$sendingAccountUsername = 'insert_your_account_here';
$sendingAccountPassword = 'insert_your_password_here';
// Recipient (To:) Details - Change this to the email details of who will receive all the emails from the website.
$recipientEmail = 'recipient@email.com'; // Where to send the admin email.
$recipientName = 'Recipient Name'; // Name of admin to receive email from website.
// Email details - Change these to suit your website needs
$emailSubject = 'A message from a form on your website'; // Subject of the email that the admin will see.
$websiteName = 'Edit your company website name'; // This is used as the "From name".
$adminEmailTemplate = 'email_to_admin.html'; // Name of template (in templates folder) to use for email to admin.
// Success Message to display in browser
$successMessage = 'Thank you, a member of our team will be in touch shortly.';
// Google reCAPTCHA
// If your form is configured with a reCAPTCHA widget, this secret key will be used to validate with Google's server.
$recaptchaSecretKey = 'optionally_insert_your_recaptcha_secret_key_here';
$recaptchaErrorMessage = 'There was a problem verifying the Google reCaptcha. Please try again.';
// Send User a Confirmation Email?
$sendConfirmationToUser = true; // leave false to disable confirmation, set to true to enable.
$userEmailField = "contact-email"; // What part of form data to use as an address to send confirmation email.
$userNameField = "contact-name"; // What part of form data to use as the user's name on confirmation email.
$confirmationEmailTemplate = "confirmation_to_user.html"; // Name of template (in templates folder) to use for email to user.
$confirmationSubject = "Thanks for testing our contact form!"; // The subject of the confirmation email.
$confirmationFromName = "A Template by Medium Rare"; // Used in the "from" field of the email.
$confirmationReplyTo = "admin@yourcompany.com"; // If the user wants to reply to the confirmation email, where should it go?
// The text replacements to use when constructing the confirmation email body
// Eg. By default, this will replace [[name]] in the email template with
// the 'name' field sent through the form. ucfirst sets the first letter to uppercase.
$confirmationReplacements = array(
"[[contact-name]]" => ucfirst($_POST["contact-name"]),
"[[mRareAddress]]" => 'http://mrare.co',
);
// Save to CSV file to keep a text record of the form entries
// This file should be password protected!
$saveToCSV = true;
$saveToCSVFileName = "csv/csv_forms_email_1.csv";
/*----------------------------------------------------------------------------*\
|* You do not need to edit anything below this line, the rest is automatic. *|
\*============================================================================*/
include('vendor/mediumrare/smtp_email.php');
?>

View file

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<h1>Thanks for submitting a form on our website.</h1>
<P>
Dear [[contact-name]],
</P>
<p>
We're glad you took the time to send us a message.
</p>
<p>
Just so you know, this is an automated email sent from our website.
</p>
<p>
We'll be in touch with you soon to respond to your enquiry.
</p>
<p>
Regards, <br />
Company Name Here <br />
</p>
</body>
</html>

View file

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<h1>EMAIL NOTICE</h1>
<p>
<ul>
<li>
Name: [[contact-name]]
</li>
<li>
Email: [[contact-email]]
</li>
<li>
Company: [[contact-company]]
</li>
<li>
Phone: [[contact-phone]]
</li>
<li>
Message: [[contact-message]]
</li>
</ul>
</p>
<h6>End of email</h6>
</body>
</html>

View file

@ -1,489 +0,0 @@
<?php
namespace DrewM\MailChimp;
/**
* Super-simple, minimum abstraction MailChimp API v3 wrapper
* MailChimp API v3: http://developer.mailchimp.com
* This wrapper: https://github.com/drewm/mailchimp-api
*
* @author Drew McLellan <drew.mclellan@gmail.com>
* @version 2.5
*/
class MailChimp
{
private $api_key;
private $api_endpoint = 'https://<dc>.api.mailchimp.com/3.0';
const TIMEOUT = 10;
/* SSL Verification
Read before disabling:
http://snippets.webaware.com.au/howto/stop-turning-off-curlopt_ssl_verifypeer-and-fix-your-php-config/
*/
public $verify_ssl = true;
private $request_successful = false;
private $last_error = '';
private $last_response = array();
private $last_request = array();
/**
* Create a new instance
*
* @param string $api_key Your MailChimp API key
* @param string $api_endpoint Optional custom API endpoint
*
* @throws \Exception
*/
public function __construct($api_key, $api_endpoint = null)
{
if (!function_exists('curl_init') || !function_exists('curl_setopt')) {
throw new \Exception("cURL support is required, but can't be found.");
}
$this->api_key = $api_key;
if ($api_endpoint === null) {
if (strpos($this->api_key, '-') === false) {
throw new \Exception("Invalid MailChimp API key supplied.");
}
list(, $data_center) = explode('-', $this->api_key);
$this->api_endpoint = str_replace('<dc>', $data_center, $this->api_endpoint);
} else {
$this->api_endpoint = $api_endpoint;
}
$this->last_response = array('headers' => null, 'body' => null);
}
/**
* Create a new instance of a Batch request. Optionally with the ID of an existing batch.
*
* @param string $batch_id Optional ID of an existing batch, if you need to check its status for example.
*
* @return Batch New Batch object.
*/
public function new_batch($batch_id = null)
{
return new Batch($this, $batch_id);
}
/**
* @return string The url to the API endpoint
*/
public function getApiEndpoint()
{
return $this->api_endpoint;
}
/**
* Convert an email address into a 'subscriber hash' for identifying the subscriber in a method URL
*
* @param string $email The subscriber's email address
*
* @return string Hashed version of the input
*/
public function subscriberHash($email)
{
return md5(strtolower($email));
}
/**
* Was the last request successful?
*
* @return bool True for success, false for failure
*/
public function success()
{
return $this->request_successful;
}
/**
* Get the last error returned by either the network transport, or by the API.
* If something didn't work, this should contain the string describing the problem.
*
* @return string|false describing the error
*/
public function getLastError()
{
return $this->last_error ?: false;
}
/**
* Get an array containing the HTTP headers and the body of the API response.
*
* @return array Assoc array with keys 'headers' and 'body'
*/
public function getLastResponse()
{
return $this->last_response;
}
/**
* Get an array containing the HTTP headers and the body of the API request.
*
* @return array Assoc array
*/
public function getLastRequest()
{
return $this->last_request;
}
/**
* Make an HTTP DELETE request - for deleting data
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (if any)
* @param int $timeout Timeout limit for request in seconds
*
* @return array|false Assoc array of API response, decoded from JSON
*/
public function delete($method, $args = array(), $timeout = self::TIMEOUT)
{
return $this->makeRequest('delete', $method, $args, $timeout);
}
/**
* Make an HTTP GET request - for retrieving data
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @param int $timeout Timeout limit for request in seconds
*
* @return array|false Assoc array of API response, decoded from JSON
*/
public function get($method, $args = array(), $timeout = self::TIMEOUT)
{
return $this->makeRequest('get', $method, $args, $timeout);
}
/**
* Make an HTTP PATCH request - for performing partial updates
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @param int $timeout Timeout limit for request in seconds
*
* @return array|false Assoc array of API response, decoded from JSON
*/
public function patch($method, $args = array(), $timeout = self::TIMEOUT)
{
return $this->makeRequest('patch', $method, $args, $timeout);
}
/**
* Make an HTTP POST request - for creating and updating items
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @param int $timeout Timeout limit for request in seconds
*
* @return array|false Assoc array of API response, decoded from JSON
*/
public function post($method, $args = array(), $timeout = self::TIMEOUT)
{
return $this->makeRequest('post', $method, $args, $timeout);
}
/**
* Make an HTTP PUT request - for creating new items
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @param int $timeout Timeout limit for request in seconds
*
* @return array|false Assoc array of API response, decoded from JSON
*/
public function put($method, $args = array(), $timeout = self::TIMEOUT)
{
return $this->makeRequest('put', $method, $args, $timeout);
}
/**
* Performs the underlying HTTP request. Not very exciting.
*
* @param string $http_verb The HTTP verb to use: get, post, put, patch, delete
* @param string $method The API method to be called
* @param array $args Assoc array of parameters to be passed
* @param int $timeout
*
* @return array|false Assoc array of decoded result
*/
private function makeRequest($http_verb, $method, $args = array(), $timeout = self::TIMEOUT)
{
$url = $this->api_endpoint . '/' . $method;
$response = $this->prepareStateForRequest($http_verb, $method, $url, $timeout);
$httpHeader = array(
'Accept: application/vnd.api+json',
'Content-Type: application/vnd.api+json',
'Authorization: apikey ' . $this->api_key
);
if (isset($args["language"])) {
$httpHeader[] = "Accept-Language: " . $args["language"];
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader);
curl_setopt($ch, CURLOPT_USERAGENT, 'DrewM/MailChimp-API/3.0 (github.com/drewm/mailchimp-api)');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ch, CURLOPT_ENCODING, '');
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
switch ($http_verb) {
case 'post':
curl_setopt($ch, CURLOPT_POST, true);
$this->attachRequestPayload($ch, $args);
break;
case 'get':
$query = http_build_query($args, '', '&');
curl_setopt($ch, CURLOPT_URL, $url . '?' . $query);
break;
case 'delete':
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
case 'patch':
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
$this->attachRequestPayload($ch, $args);
break;
case 'put':
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
$this->attachRequestPayload($ch, $args);
break;
}
$responseContent = curl_exec($ch);
$response['headers'] = curl_getinfo($ch);
$response = $this->setResponseState($response, $responseContent, $ch);
$formattedResponse = $this->formatResponse($response);
curl_close($ch);
$isSuccess = $this->determineSuccess($response, $formattedResponse, $timeout);
return is_array($formattedResponse) ? $formattedResponse : $isSuccess;
}
/**
* @param string $http_verb
* @param string $method
* @param string $url
* @param integer $timeout
*
* @return array
*/
private function prepareStateForRequest($http_verb, $method, $url, $timeout)
{
$this->last_error = '';
$this->request_successful = false;
$this->last_response = array(
'headers' => null, // array of details from curl_getinfo()
'httpHeaders' => null, // array of HTTP headers
'body' => null // content of the response
);
$this->last_request = array(
'method' => $http_verb,
'path' => $method,
'url' => $url,
'body' => '',
'timeout' => $timeout,
);
return $this->last_response;
}
/**
* Get the HTTP headers as an array of header-name => header-value pairs.
*
* The "Link" header is parsed into an associative array based on the
* rel names it contains. The original value is available under
* the "_raw" key.
*
* @param string $headersAsString
*
* @return array
*/
private function getHeadersAsArray($headersAsString)
{
$headers = array();
foreach (explode("\r\n", $headersAsString) as $i => $line) {
if ($i === 0) { // HTTP code
continue;
}
$line = trim($line);
if (empty($line)) {
continue;
}
list($key, $value) = explode(': ', $line);
if ($key == 'Link') {
$value = array_merge(
array('_raw' => $value),
$this->getLinkHeaderAsArray($value)
);
}
$headers[$key] = $value;
}
return $headers;
}
/**
* Extract all rel => URL pairs from the provided Link header value
*
* Mailchimp only implements the URI reference and relation type from
* RFC 5988, so the value of the header is something like this:
*
* 'https://us13.api.mailchimp.com/schema/3.0/Lists/Instance.json; rel="describedBy",
* <https://us13.admin.mailchimp.com/lists/members/?id=XXXX>; rel="dashboard"'
*
* @param string $linkHeaderAsString
*
* @return array
*/
private function getLinkHeaderAsArray($linkHeaderAsString)
{
$urls = array();
if (preg_match_all('/<(.*?)>\s*;\s*rel="(.*?)"\s*/', $linkHeaderAsString, $matches)) {
foreach ($matches[2] as $i => $relName) {
$urls[$relName] = $matches[1][$i];
}
}
return $urls;
}
/**
* Encode the data and attach it to the request
*
* @param resource $ch cURL session handle, used by reference
* @param array $data Assoc array of data to attach
*/
private function attachRequestPayload(&$ch, $data)
{
$encoded = json_encode($data);
$this->last_request['body'] = $encoded;
curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded);
}
/**
* Decode the response and format any error messages for debugging
*
* @param array $response The response from the curl request
*
* @return array|false The JSON decoded into an array
*/
private function formatResponse($response)
{
$this->last_response = $response;
if (!empty($response['body'])) {
return json_decode($response['body'], true);
}
return false;
}
/**
* Do post-request formatting and setting state from the response
*
* @param array $response The response from the curl request
* @param string $responseContent The body of the response from the curl request
* @param resource $ch The curl resource
*
* @return array The modified response
*/
private function setResponseState($response, $responseContent, $ch)
{
if ($responseContent === false) {
$this->last_error = curl_error($ch);
} else {
$headerSize = $response['headers']['header_size'];
$response['httpHeaders'] = $this->getHeadersAsArray(substr($responseContent, 0, $headerSize));
$response['body'] = substr($responseContent, $headerSize);
if (isset($response['headers']['request_header'])) {
$this->last_request['headers'] = $response['headers']['request_header'];
}
}
return $response;
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @param array $response The response from the curl request
* @param array|false $formattedResponse The response body payload from the curl request
* @param int $timeout The timeout supplied to the curl request.
*
* @return bool If the request was successful
*/
private function determineSuccess($response, $formattedResponse, $timeout)
{
$status = $this->findHTTPStatus($response, $formattedResponse);
if ($status >= 200 && $status <= 299) {
$this->request_successful = true;
return true;
}
if (isset($formattedResponse['detail'])) {
$this->last_error = sprintf('%d: %s', $formattedResponse['status'], $formattedResponse['detail']);
return false;
}
if ($timeout > 0 && $response['headers'] && $response['headers']['total_time'] >= $timeout) {
$this->last_error = sprintf('Request timed out after %f seconds.', $response['headers']['total_time']);
return false;
}
$this->last_error = 'Unknown error, call getLastResponse() to find out what happened.';
return false;
}
/**
* Find the HTTP status code from the headers or API response body
*
* @param array $response The response from the curl request
* @param array|false $formattedResponse The response body payload from the curl request
*
* @return int HTTP status code
*/
private function findHTTPStatus($response, $formattedResponse)
{
if (!empty($response['headers']) && isset($response['headers']['http_code'])) {
return (int)$response['headers']['http_code'];
}
if (!empty($response['body']) && isset($formattedResponse['status'])) {
return (int)$formattedResponse['status'];
}
return 418;
}
}

View file

View file

@ -1,26 +0,0 @@
<?php
// Get Basic HTML template for sending email with user's input.
$pathToTemplate = 'templates/'.$adminEmailTemplate;
set_error_handler(
function ($severity, $message, $file, $line) {
$response->status = 'error';
$response->message = 'Failed to open admin email template file. Email to admin was not sent. Error message:'.$message;
echo(json_encode($response));
exit;
}
);
try{
$adminEmailTemplate = file_get_contents($pathToTemplate);
} catch (Exception $err) {
exit;
}
$postValues = array_values($_POST);
// Take all Post array keys and
$postKeys = array_map(function($value) {return '[['.$value.']]';}, array_keys($_POST));
$htmlContent = str_replace($postKeys, $postValues, $adminEmailTemplate);

View file

@ -1,26 +0,0 @@
<?php
// Get HTML template to send to User as confirmation.
$pathToTemplate = 'templates/'.$confirmationEmailTemplate;
set_error_handler(
function ($severity, $message, $file, $line) {
$response->status = 'error';
$response->message = 'Failed to open user confirmation email template file. Confirmation was not sent. ';
echo(json_encode($response));
exit;
}
);
try{
$confirmationEmailTemplate = file_get_contents($pathToTemplate);
} catch (Exception $err) {
exit;
}
restore_error_handler();
$confirmationHtmlContent = strtr($confirmationEmailTemplate, $confirmationReplacements);
$confirmationTextContent = filter_var($confirmationHtmlContent, FILTER_SANITIZE_STRING);

View file

@ -1,34 +0,0 @@
<?php
// If the form has been submitted with a captcha, check it - if it fails from Google, exit the script after returning an error message.
$data = array(
'secret' => $recaptchaSecretKey,
'response' => $_POST['g-recaptcha-response']
);
$verify = curl_init();
curl_setopt($verify, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
curl_setopt($verify, CURLOPT_POST, true);
curl_setopt($verify, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($verify, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($verify, CURLOPT_RETURNTRANSFER, true);
$gResponse = curl_exec($verify);
$gResponse = json_decode( $gResponse , true );
if($gResponse['success'] == false)
{
$response->status = "error";
$response->message = $recaptchaErrorMessage;
$response->errorDetail = json_encode($gResponse);
$response->errorName = 'Google reCAPTCHA verification error';
echo(json_encode($response));
exit;
}else{
$response->recaptchaDetail = $gResponse;
unset($_POST['g-recaptcha-response']);
}
?>

View file

@ -1,16 +0,0 @@
<?php
// Basic sanitization to remove tags to safeguard email
function util_array_trim(array &$array, $filter = false)
{
array_walk_recursive($array, function (&$value) use ($filter) {
$value = trim($value);
if ($filter) {
$value = filter_var($value, FILTER_SANITIZE_STRING);
}
});
return $array;
}
$_POST = util_array_trim($_POST, true);

View file

@ -1,16 +0,0 @@
<?php
// Save to CSV file
$file = fopen($saveToCSVFileName, 'a');
$data = array_values($_POST);
$data = array_merge(array( date("Y-m-d H:i:s")), $data);
fputcsv_eol($file, $data,"\n");
fclose($file);
function fputcsv_eol($fp, $array, $eol) {
fputcsv($fp, $array,',', '"');
if("\n" != $eol && 0 === fseek($fp, -1, SEEK_CUR)) {
fwrite($fp, $eol);
}
}

View file

@ -1,34 +0,0 @@
<?php
// Require the Swift Mailer library
require_once 'vendor/swiftmailer/swift_required.php';
$transport = Swift_SmtpTransport::newInstance( $outgoingServerAddress, $outgoingServerPort, $outgoingServerSecurity )
->setUsername( $sendingAccountUsername )
->setPassword( $sendingAccountPassword );
$mailer = Swift_Mailer::newInstance($transport);
$fromArray = array($sendingAccountUsername => $websiteName);
$sentMessages = 0;
$message = Swift_Message::newInstance($emailSubject)
->setSender(array($sendingAccountUsername => $websiteName))
->setFrom($fromArray)
->setReplyTo($_POST[$userEmailField])
->setTo(array($recipientEmail => $recipientName))
->setBody($textContent, 'text/plain')
->addPart($htmlContent, 'text/html');
// Send the message or catch an error if it occurs.
try{
$sentMessages = $mailer->send($message);
$response->status = "success";
} catch(Exception $e){
$response->status = "error";
$response->message = $e->getMessage();
echo(json_encode($response));
}
?>

View file

@ -1,29 +0,0 @@
<?php
// Require the Swift Mailer library
require_once 'vendor/swiftmailer/swift_required.php';
$mailer = Swift_Mailer::newInstance($transport);
$fromArray = array($sendingAccountUsername => $confirmationFromName);
$message = Swift_Message::newInstance($confirmationSubject)
->setSender(array($sendingAccountUsername => $confirmationFromName))
->setFrom($fromArray)
->setReplyTo($confirmationReplyTo)
->setTo(array($_POST[$userEmailField] => $_POST[$userNameField]))
->setBody($confirmationTextContent, 'text/plain')
->addPart($confirmationHtmlContent, 'text/html');
// Send the message or catch an error if it occurs.
try{
$sentMessages = $mailer->send($message);
$response->status = "success";
} catch(Exception $e){
$response->status = "error";
$response->message = $e->getMessage();
echo(json_encode($response));
}
?>

View file

@ -1,15 +0,0 @@
<?php
$textContent = "";
// Creating the message text using fields sent through POST
foreach ($_POST as $key => $value)
{
if($key !== 'g-recaptcha-response' && $key !== 'captcha'){// Sets of checkboxes will be shown as comma-separated values as they are passed in as an array.
if(is_array($value)){
$value = implode(', ' , $value);
}
$textContent .= ucfirst($key).": ".$value.PHP_EOL;
}
}
?>

View file

@ -1,82 +0,0 @@
<?php
//error_reporting(-1);
//ini_set('display_errors', 'On');
// Include the MailChimp API wrapper
// https://github.com/drewm/mailchimp-api
include('vendor/drewm/MailChimp.php');
use \DrewM\MailChimp\MailChimp;
$MailChimp = new MailChimp($apiKey);
// Set up response object to be returned to browser as JSON
$response = (object) array('status' => '', 'message' => $successMessage);
// If the form has been submitted with a captcha, verify it - if it fails from Google,
// exit the script after returning an error message.
if(isset($_POST['g-recaptcha-response'])){
require('include/recaptcha_v2.php');
}
// Make sure there are no blank fields which will cause an error in MailChimp
foreach ($mergeFields as $key => $value)
{
if(empty($value)){
$mergeFields[$key] = ' ';
}
}
// Check if email is subscribed to the list already
$subscriber_hash = $MailChimp->subscriberHash($_POST[$emailField]);
$result = $MailChimp->get("lists/$listId/members/$subscriber_hash");
// Check result of MailChimp operation
if ($MailChimp->success()) {
// Success message
$response->status = "success";
if(json_decode($MailChimp->getLastResponse()['body'])->status === 'subscribed'){
$response->message = $alreadySubscribed;
}
if(json_decode($MailChimp->getLastResponse()['body'])->status === 'pending'){
$response->message = $checkConfirmation;
}
echo json_encode($response);
exit;
} else {
// If not found in list, it is 404 and the script should continue else, show the error.
if(json_decode($MailChimp->getLastResponse()['body'])->status !== 404){
handle_error($MailChimp);
}
}
// Submit subscriber data to MailChimp
// For parameters doc, refer to: http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/
// For wrapper's doc, visit: https://github.com/drewm/mailchimp-api
$result = $MailChimp->post("lists/$listId/members", [
'email_address' => $_POST[$emailField],
'mergeFields' => $mergeFields,
'status' => $status,
]);
// Check result of MailChimp operation
if ($MailChimp->success()) {
// Success message
$response->status = "success";
$response->message = $successMessage;
} else {
// Display error
handle_error($MailChimp);
}
echo json_encode($response);
exit;
function handle_error($MailChimp) {
$response->status = "error";
$response->message = $MailChimp->getLastError();
$response->errorDetail = $MailChimp->getLastResponse()['body'];
$response->errorName = 'MailChimp subscribe error';
echo json_encode($response);
exit;
}
?>

View file

@ -1,50 +0,0 @@
<?php
//error_reporting(-1);
//ini_set('display_errors', 'On');
// Basic sanitization to remove tags from user input
require('include/sanitize_post.php');
// Set default timezone as some servers do not have this set.
if(isset($timeZone) && $timeZone != ""){
date_default_timezone_set($timeZone);
}
else{
date_default_timezone_set("UTC");
}
// Set up response object to be returned to browser as JSON
$response = (object) array('status' => '', 'message' => $successMessage);
// If the form has been submitted with a captcha, verify it - if it fails from Google,
// exit the script after returning an error message.
if(isset($_POST['g-recaptcha-response'])){
require('include/recaptcha_v2.php');
}
// Load template and make replacements
require('include/load_template_admin.php');
// Assemble the text for the plain-text version of the message.
require('include/text_content.php');
// Send the email via SMTP using Swiftmailer
require('include/send_smtp_admin.php');
if($saveToCSV === true){
require('include/save_csv.php');
}
// Send confirmation email to user
if($sendConfirmationToUser === true){
// Load user confirmation template and makre replacements
require('include/load_template_user.php');
// Send the email to the user
require('include/send_smtp_user.php');
}
echo(json_encode($response));
exit;
?>

View file

@ -1,80 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* General utility class in Swift Mailer, not to be instantiated.
*
*
* @author Chris Corbyn
*/
abstract class Swift
{
public static $initialized = false;
public static $inits = array();
/** Swift Mailer Version number generated during dist release process */
const VERSION = '@SWIFT_VERSION_NUMBER@';
/**
* Registers an initializer callable that will be called the first time
* a SwiftMailer class is autoloaded.
*
* This enables you to tweak the default configuration in a lazy way.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function init($callable)
{
self::$inits[] = $callable;
}
/**
* Internal autoloader for spl_autoload_register().
*
* @param string $class
*/
public static function autoload($class)
{
// Don't interfere with other autoloaders
if (0 !== strpos($class, 'Swift_')) {
return;
}
$path = dirname(__FILE__).'/'.str_replace('_', '/', $class).'.php';
if (!file_exists($path)) {
return;
}
require $path;
if (self::$inits && !self::$initialized) {
self::$initialized = true;
foreach (self::$inits as $init) {
call_user_func($init);
}
}
}
/**
* Configure autoloading using Swift Mailer.
*
* This is designed to play nicely with other autoloaders.
*
* @param mixed $callable A valid PHP callable that will be called when autoloading the first Swift class
*/
public static function registerAutoload($callable = null)
{
if (null !== $callable) {
self::$inits[] = $callable;
}
spl_autoload_register(array('Swift', 'autoload'));
}
}

View file

@ -1,71 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Attachment class for attaching files to a {@link Swift_Mime_Message}.
*
* @author Chris Corbyn
*/
class Swift_Attachment extends Swift_Mime_Attachment
{
/**
* Create a new Attachment.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
call_user_func_array(
array($this, 'Swift_Mime_Attachment::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.attachment')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new Attachment.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return Swift_Mime_Attachment
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new Attachment from a filesystem path.
*
* @param string $path
* @param string $contentType optional
*
* @return Swift_Mime_Attachment
*/
public static function fromPath($path, $contentType = null)
{
return self::newInstance()->setFile(
new Swift_ByteStream_FileByteStream($path),
$contentType
);
}
}

View file

@ -1,179 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides the base functionality for an InputStream supporting filters.
*
* @author Chris Corbyn
*/
abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_InputByteStream, Swift_Filterable
{
/**
* Write sequence.
*/
protected $_sequence = 0;
/**
* StreamFilters.
*/
private $_filters = array();
/**
* A buffer for writing.
*/
private $_writeBuffer = '';
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $_mirrors = array();
/**
* Commit the given bytes to the storage medium immediately.
*
* @param string $bytes
*/
abstract protected function _commit($bytes);
/**
* Flush any buffers/content with immediate effect.
*/
abstract protected function _flush();
/**
* Add a StreamFilter to this InputByteStream.
*
* @param Swift_StreamFilter $filter
* @param string $key
*/
public function addFilter(Swift_StreamFilter $filter, $key)
{
$this->_filters[$key] = $filter;
}
/**
* Remove an already present StreamFilter based on its $key.
*
* @param string $key
*/
public function removeFilter($key)
{
unset($this->_filters[$key]);
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*
* @return int
*
* @throws Swift_IoException
*/
public function write($bytes)
{
$this->_writeBuffer .= $bytes;
foreach ($this->_filters as $filter) {
if ($filter->shouldBuffer($this->_writeBuffer)) {
return;
}
}
$this->_doWrite($this->_writeBuffer);
return ++$this->_sequence;
}
/**
* For any bytes that are currently buffered inside the stream, force them
* off the buffer.
*
* @throws Swift_IoException
*/
public function commit()
{
$this->_doWrite($this->_writeBuffer);
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is)
{
$this->_mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->_mirrors as $k => $stream) {
if ($is === $stream) {
if ($this->_writeBuffer !== '') {
$stream->write($this->_writeBuffer);
}
unset($this->_mirrors[$k]);
}
}
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*
* @throws Swift_IoException
*/
public function flushBuffers()
{
if ($this->_writeBuffer !== '') {
$this->_doWrite($this->_writeBuffer);
}
$this->_flush();
foreach ($this->_mirrors as $stream) {
$stream->flushBuffers();
}
}
/** Run $bytes through all filters */
private function _filter($bytes)
{
foreach ($this->_filters as $filter) {
$bytes = $filter->filter($bytes);
}
return $bytes;
}
/** Just write the bytes to the stream */
private function _doWrite($bytes)
{
$this->_commit($this->_filter($bytes));
foreach ($this->_mirrors as $stream) {
$stream->write($bytes);
}
$this->_writeBuffer = '';
}
}

View file

@ -1,184 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from an array.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_OutputByteStream
{
/**
* The internal stack of bytes.
*
* @var string[]
*/
private $_array = array();
/**
* The size of the stack
*
* @var int
*/
private $_arraySize = 0;
/**
* The internal pointer offset.
*
* @var int
*/
private $_offset = 0;
/**
* Bound streams.
*
* @var Swift_InputByteStream[]
*/
private $_mirrors = array();
/**
* Create a new ArrayByteStream.
*
* If $stack is given the stream will be populated with the bytes it contains.
*
* @param mixed $stack of bytes in string or array form, optional
*/
public function __construct($stack = null)
{
if (is_array($stack)) {
$this->_array = $stack;
$this->_arraySize = count($stack);
} elseif (is_string($stack)) {
$this->write($stack);
} else {
$this->_array = array();
}
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_offset == $this->_arraySize) {
return false;
}
// Don't use array slice
$end = $length + $this->_offset;
$end = $this->_arraySize<$end
?$this->_arraySize
:$end;
$ret = '';
for (; $this->_offset < $end; ++$this->_offset) {
$ret .= $this->_array[$this->_offset];
}
return $ret;
}
/**
* Writes $bytes to the end of the stream.
*
* @param string $bytes
*/
public function write($bytes)
{
$to_add = str_split($bytes);
foreach ($to_add as $value) {
$this->_array[] = $value;
}
$this->_arraySize = count($this->_array);
foreach ($this->_mirrors as $stream) {
$stream->write($bytes);
}
}
/**
* Not used.
*/
public function commit()
{
}
/**
* Attach $is to this stream.
*
* The stream acts as an observer, receiving all data that is written.
* All {@link write()} and {@link flushBuffers()} operations will be mirrored.
*
* @param Swift_InputByteStream $is
*/
public function bind(Swift_InputByteStream $is)
{
$this->_mirrors[] = $is;
}
/**
* Remove an already bound stream.
*
* If $is is not bound, no errors will be raised.
* If the stream currently has any buffered data it will be written to $is
* before unbinding occurs.
*
* @param Swift_InputByteStream $is
*/
public function unbind(Swift_InputByteStream $is)
{
foreach ($this->_mirrors as $k => $stream) {
if ($is === $stream) {
unset($this->_mirrors[$k]);
}
}
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if ($byteOffset > $this->_arraySize) {
$byteOffset = $this->_arraySize;
} elseif ($byteOffset < 0) {
$byteOffset = 0;
}
$this->_offset = $byteOffset;
}
/**
* Flush the contents of the stream (empty it) and set the internal pointer
* to the beginning.
*/
public function flushBuffers()
{
$this->_offset = 0;
$this->_array = array();
$this->_arraySize = 0;
foreach ($this->_mirrors as $stream) {
$stream->flushBuffers();
}
}
}

View file

@ -1,229 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Allows reading and writing of bytes to and from a file.
*
* @author Chris Corbyn
*/
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream
{
/** The internal pointer offset */
private $_offset = 0;
/** The path to the file */
private $_path;
/** The mode this file is opened in for writing */
private $_mode;
/** A lazy-loaded resource handle for reading the file */
private $_reader;
/** A lazy-loaded resource handle for writing the file */
private $_writer;
/** If magic_quotes_runtime is on, this will be true */
private $_quotes = false;
/** If stream is seekable true/false, or null if not known */
private $_seekable = null;
/**
* Create a new FileByteStream for $path.
*
* @param string $path
* @param bool $writable if true
*/
public function __construct($path, $writable = false)
{
if (empty($path)) {
throw new Swift_IoException('The path cannot be empty');
}
$this->_path = $path;
$this->_mode = $writable ? 'w+b' : 'rb';
if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
$this->_quotes = true;
}
}
/**
* Get the complete path to the file.
*
* @return string
*/
public function getPath()
{
return $this->_path;
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the
* remaining bytes are given instead. If no bytes are remaining at all, boolean
* false is returned.
*
* @param int $length
*
* @return string|bool
*
* @throws Swift_IoException
*/
public function read($length)
{
$fp = $this->_getReadHandle();
if (!feof($fp)) {
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 0);
}
$bytes = fread($fp, $length);
if ($this->_quotes) {
ini_set('magic_quotes_runtime', 1);
}
$this->_offset = ftell($fp);
// If we read one byte after reaching the end of the file
// feof() will return false and an empty string is returned
if ($bytes === '' && feof($fp)) {
$this->_resetReadHandle();
return false;
}
return $bytes;
}
$this->_resetReadHandle();
return false;
}
/**
* Move the internal read pointer to $byteOffset in the stream.
*
* @param int $byteOffset
*
* @return bool
*/
public function setReadPointer($byteOffset)
{
if (isset($this->_reader)) {
$this->_seekReadStreamToPosition($byteOffset);
}
$this->_offset = $byteOffset;
}
/** Just write the bytes to the file */
protected function _commit($bytes)
{
fwrite($this->_getWriteHandle(), $bytes);
$this->_resetReadHandle();
}
/** Not used */
protected function _flush()
{
}
/** Get the resource for reading */
private function _getReadHandle()
{
if (!isset($this->_reader)) {
if (!$this->_reader = fopen($this->_path, 'rb')) {
throw new Swift_IoException(
'Unable to open file for reading [' . $this->_path . ']'
);
}
if ($this->_offset <> 0) {
$this->_getReadStreamSeekableStatus();
$this->_seekReadStreamToPosition($this->_offset);
}
}
return $this->_reader;
}
/** Get the resource for writing */
private function _getWriteHandle()
{
if (!isset($this->_writer)) {
if (!$this->_writer = fopen($this->_path, $this->_mode)) {
throw new Swift_IoException(
'Unable to open file for writing [' . $this->_path . ']'
);
}
}
return $this->_writer;
}
/** Force a reload of the resource for reading */
private function _resetReadHandle()
{
if (isset($this->_reader)) {
fclose($this->_reader);
$this->_reader = null;
}
}
/** Check if ReadOnly Stream is seekable */
private function _getReadStreamSeekableStatus()
{
$metas = stream_get_meta_data($this->_reader);
$this->_seekable = $metas['seekable'];
}
/** Streams in a readOnly stream ensuring copy if needed */
private function _seekReadStreamToPosition($offset)
{
if ($this->_seekable===null) {
$this->_getReadStreamSeekableStatus();
}
if ($this->_seekable === false) {
$currentPos = ftell($this->_reader);
if ($currentPos<$offset) {
$toDiscard = $offset-$currentPos;
fread($this->_reader, $toDiscard);
return;
}
$this->_copyReadStream();
}
fseek($this->_reader, $offset, SEEK_SET);
}
/** Copy a readOnly Stream to ensure seekability */
private function _copyReadStream()
{
if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) {
/* We have opened a php:// Stream Should work without problem */
} elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) {
/* We have opened a tmpfile */
} else {
throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
}
$currentPos = ftell($this->_reader);
fclose($this->_reader);
$source = fopen($this->_path, 'rb');
if (!$source) {
throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']');
}
fseek($tmpFile, 0, SEEK_SET);
while (!feof($source)) {
fwrite($tmpFile, fread($source, 4096));
}
fseek($tmpFile, $currentPos, SEEK_SET);
fclose($source);
$this->_reader = $tmpFile;
}
}

View file

@ -1,42 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* @author Romain-Geissler
*/
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream
{
public function __construct()
{
$filePath = tempnam(sys_get_temp_dir(), 'FileByteStream');
if ($filePath === false) {
throw new Swift_IoException('Failed to retrieve temporary file name.');
}
parent::__construct($filePath, true);
}
public function getContent()
{
if (($content = file_get_contents($this->getPath())) === false) {
throw new Swift_IoException('Failed to get temporary file content.');
}
return $content;
}
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}
}

View file

@ -1,67 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes characters for a specific character set.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
interface Swift_CharacterReader
{
const MAP_TYPE_INVALID = 0x01;
const MAP_TYPE_FIXED_LEN = 0x02;
const MAP_TYPE_POSITIONS = 0x03;
/**
* Returns the complete character map
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars);
/**
* Returns the mapType, see constants.
*
* @return int
*/
public function getMapType();
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param integer[] $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size);
/**
* Returns the number of bytes which should be read to start each character.
*
* For fixed width character sets this should be the number of octets-per-character.
* For multibyte character sets this will probably be 1.
*
* @return int
*/
public function getInitialByteSize();
}

View file

@ -1,97 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides fixed-width byte sizes for reading fixed-width character sets.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader
{
/**
* The number of bytes in a single character.
*
* @var int
*/
private $_width;
/**
* Creates a new GenericFixedWidthReader using $width bytes per character.
*
* @param int $width
*/
public function __construct($width)
{
$this->_width = $width;
}
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen = strlen($string);
// % and / are CPU intensive, so, maybe find a better way
$ignored = $strlen % $this->_width;
$ignoredChars = substr($string, - $ignored);
$currentMap = $this->_width;
return ($strlen - $ignored) / $this->_width;
}
/**
* Returns the mapType.
*
* @return int
*/
public function getMapType()
{
return self::MAP_TYPE_FIXED_LEN;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
*
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$needed = $this->_width - $size;
return ($needed > -1) ? $needed : -1;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return $this->_width;
}
}

View file

@ -1,83 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes US-ASCII characters.
*
* @author Chris Corbyn
*/
class Swift_CharacterReader_UsAsciiReader implements Swift_CharacterReader
{
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param string $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
$strlen=strlen($string);
$ignoredChars='';
for ($i = 0; $i < $strlen; ++$i) {
if ($string[$i]>"\x07F") { // Invalid char
$currentMap[$i+$startOffset]=$string[$i];
}
}
return $strlen;
}
/**
* Returns mapType
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_INVALID;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
$byte = reset($bytes);
if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) {
return 0;
} else {
return -1;
}
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}

View file

@ -1,179 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Analyzes UTF-8 characters.
*
* @author Chris Corbyn
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader
{
/** Pre-computed for optimization */
private static $length_map=array(
// N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6N
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7N
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8N
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9N
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAN
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBN
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCN
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDN
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEN
4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0 // 0xFN
);
private static $s_length_map=array(
"\x00"=>1, "\x01"=>1, "\x02"=>1, "\x03"=>1, "\x04"=>1, "\x05"=>1, "\x06"=>1, "\x07"=>1,
"\x08"=>1, "\x09"=>1, "\x0a"=>1, "\x0b"=>1, "\x0c"=>1, "\x0d"=>1, "\x0e"=>1, "\x0f"=>1,
"\x10"=>1, "\x11"=>1, "\x12"=>1, "\x13"=>1, "\x14"=>1, "\x15"=>1, "\x16"=>1, "\x17"=>1,
"\x18"=>1, "\x19"=>1, "\x1a"=>1, "\x1b"=>1, "\x1c"=>1, "\x1d"=>1, "\x1e"=>1, "\x1f"=>1,
"\x20"=>1, "\x21"=>1, "\x22"=>1, "\x23"=>1, "\x24"=>1, "\x25"=>1, "\x26"=>1, "\x27"=>1,
"\x28"=>1, "\x29"=>1, "\x2a"=>1, "\x2b"=>1, "\x2c"=>1, "\x2d"=>1, "\x2e"=>1, "\x2f"=>1,
"\x30"=>1, "\x31"=>1, "\x32"=>1, "\x33"=>1, "\x34"=>1, "\x35"=>1, "\x36"=>1, "\x37"=>1,
"\x38"=>1, "\x39"=>1, "\x3a"=>1, "\x3b"=>1, "\x3c"=>1, "\x3d"=>1, "\x3e"=>1, "\x3f"=>1,
"\x40"=>1, "\x41"=>1, "\x42"=>1, "\x43"=>1, "\x44"=>1, "\x45"=>1, "\x46"=>1, "\x47"=>1,
"\x48"=>1, "\x49"=>1, "\x4a"=>1, "\x4b"=>1, "\x4c"=>1, "\x4d"=>1, "\x4e"=>1, "\x4f"=>1,
"\x50"=>1, "\x51"=>1, "\x52"=>1, "\x53"=>1, "\x54"=>1, "\x55"=>1, "\x56"=>1, "\x57"=>1,
"\x58"=>1, "\x59"=>1, "\x5a"=>1, "\x5b"=>1, "\x5c"=>1, "\x5d"=>1, "\x5e"=>1, "\x5f"=>1,
"\x60"=>1, "\x61"=>1, "\x62"=>1, "\x63"=>1, "\x64"=>1, "\x65"=>1, "\x66"=>1, "\x67"=>1,
"\x68"=>1, "\x69"=>1, "\x6a"=>1, "\x6b"=>1, "\x6c"=>1, "\x6d"=>1, "\x6e"=>1, "\x6f"=>1,
"\x70"=>1, "\x71"=>1, "\x72"=>1, "\x73"=>1, "\x74"=>1, "\x75"=>1, "\x76"=>1, "\x77"=>1,
"\x78"=>1, "\x79"=>1, "\x7a"=>1, "\x7b"=>1, "\x7c"=>1, "\x7d"=>1, "\x7e"=>1, "\x7f"=>1,
"\x80"=>0, "\x81"=>0, "\x82"=>0, "\x83"=>0, "\x84"=>0, "\x85"=>0, "\x86"=>0, "\x87"=>0,
"\x88"=>0, "\x89"=>0, "\x8a"=>0, "\x8b"=>0, "\x8c"=>0, "\x8d"=>0, "\x8e"=>0, "\x8f"=>0,
"\x90"=>0, "\x91"=>0, "\x92"=>0, "\x93"=>0, "\x94"=>0, "\x95"=>0, "\x96"=>0, "\x97"=>0,
"\x98"=>0, "\x99"=>0, "\x9a"=>0, "\x9b"=>0, "\x9c"=>0, "\x9d"=>0, "\x9e"=>0, "\x9f"=>0,
"\xa0"=>0, "\xa1"=>0, "\xa2"=>0, "\xa3"=>0, "\xa4"=>0, "\xa5"=>0, "\xa6"=>0, "\xa7"=>0,
"\xa8"=>0, "\xa9"=>0, "\xaa"=>0, "\xab"=>0, "\xac"=>0, "\xad"=>0, "\xae"=>0, "\xaf"=>0,
"\xb0"=>0, "\xb1"=>0, "\xb2"=>0, "\xb3"=>0, "\xb4"=>0, "\xb5"=>0, "\xb6"=>0, "\xb7"=>0,
"\xb8"=>0, "\xb9"=>0, "\xba"=>0, "\xbb"=>0, "\xbc"=>0, "\xbd"=>0, "\xbe"=>0, "\xbf"=>0,
"\xc0"=>2, "\xc1"=>2, "\xc2"=>2, "\xc3"=>2, "\xc4"=>2, "\xc5"=>2, "\xc6"=>2, "\xc7"=>2,
"\xc8"=>2, "\xc9"=>2, "\xca"=>2, "\xcb"=>2, "\xcc"=>2, "\xcd"=>2, "\xce"=>2, "\xcf"=>2,
"\xd0"=>2, "\xd1"=>2, "\xd2"=>2, "\xd3"=>2, "\xd4"=>2, "\xd5"=>2, "\xd6"=>2, "\xd7"=>2,
"\xd8"=>2, "\xd9"=>2, "\xda"=>2, "\xdb"=>2, "\xdc"=>2, "\xdd"=>2, "\xde"=>2, "\xdf"=>2,
"\xe0"=>3, "\xe1"=>3, "\xe2"=>3, "\xe3"=>3, "\xe4"=>3, "\xe5"=>3, "\xe6"=>3, "\xe7"=>3,
"\xe8"=>3, "\xe9"=>3, "\xea"=>3, "\xeb"=>3, "\xec"=>3, "\xed"=>3, "\xee"=>3, "\xef"=>3,
"\xf0"=>4, "\xf1"=>4, "\xf2"=>4, "\xf3"=>4, "\xf4"=>4, "\xf5"=>4, "\xf6"=>4, "\xf7"=>4,
"\xf8"=>5, "\xf9"=>5, "\xfa"=>5, "\xfb"=>5, "\xfc"=>6, "\xfd"=>6, "\xfe"=>0, "\xff"=>0,
);
/**
* Returns the complete character map.
*
* @param string $string
* @param int $startOffset
* @param array $currentMap
* @param mixed $ignoredChars
*
* @return int
*/
public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars)
{
if (!isset($currentMap['i']) || ! isset($currentMap['p'])) {
$currentMap['p'] = $currentMap['i'] = array();
}
$strlen=strlen($string);
$charPos=count($currentMap['p']);
$foundChars=0;
$invalid=false;
for ($i = 0; $i < $strlen; ++$i) {
$char = $string[$i];
$size = self::$s_length_map[$char];
if ($size == 0) {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue;
} else {
if ($invalid == true) {
/* We mark the chars as invalid and start a new char */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i;
$currentMap['i'][$charPos + $foundChars] = true;
++$foundChars;
$invalid = false;
}
if (($i + $size) > $strlen) {
$ignoredChars = substr($string, $i);
break;
}
for ($j = 1; $j < $size; ++$j) {
$char = $string[$i + $j];
if ($char > "\x7F" && $char < "\xC0") {
// Valid - continue parsing
} else {
/* char is invalid, we must wait for a resync */
$invalid = true;
continue 2;
}
}
/* Ok we got a complete char here */
$currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size;
$i += $j - 1;
++$foundChars;
}
}
return $foundChars;
}
/**
* Returns mapType.
*
* @return int mapType
*/
public function getMapType()
{
return self::MAP_TYPE_POSITIONS;
}
/**
* Returns an integer which specifies how many more bytes to read.
*
* A positive integer indicates the number of more bytes to fetch before invoking
* this method again.
* A value of zero means this is already a valid character.
* A value of -1 means this cannot possibly be a valid character.
*
* @param string $bytes
* @param int $size
*
* @return int
*/
public function validateByteSequence($bytes, $size)
{
if ($size<1) {
return -1;
}
$needed = self::$length_map[$bytes[0]] - $size;
return ($needed > -1)
? $needed
: -1
;
}
/**
* Returns the number of bytes which should be read to start each character.
*
* @return int
*/
public function getInitialByteSize()
{
return 1;
}
}

View file

@ -1,26 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
interface Swift_CharacterReaderFactory
{
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset);
}

View file

@ -1,124 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Standard factory for creating CharacterReaders.
*
* @author Chris Corbyn
*/
class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift_CharacterReaderFactory
{
/**
* A map of charset patterns to their implementation classes.
*
* @var array
*/
private static $_map = array();
/**
* Factories which have already been loaded.
*
* @var Swift_CharacterReaderFactory[]
*/
private static $_loaded = array();
/**
* Creates a new CharacterReaderFactory.
*/
public function __construct()
{
$this->init();
}
public function __wakeup()
{
$this->init();
}
public function init()
{
if (count(self::$_map) > 0) {
return;
}
$prefix = 'Swift_CharacterReader_';
$singleByte = array(
'class' => $prefix . 'GenericFixedWidthReader',
'constructor' => array(1)
);
$doubleByte = array(
'class' => $prefix . 'GenericFixedWidthReader',
'constructor' => array(2)
);
$fourBytes = array(
'class' => $prefix . 'GenericFixedWidthReader',
'constructor' => array(4)
);
// Utf-8
self::$_map['utf-?8'] = array(
'class' => $prefix . 'Utf8Reader',
'constructor' => array()
);
//7-8 bit charsets
self::$_map['(us-)?ascii'] = $singleByte;
self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte;
self::$_map['windows-?125[0-9]'] = $singleByte;
self::$_map['cp-?[0-9]+'] = $singleByte;
self::$_map['ansi'] = $singleByte;
self::$_map['macintosh'] = $singleByte;
self::$_map['koi-?7'] = $singleByte;
self::$_map['koi-?8-?.+'] = $singleByte;
self::$_map['mik'] = $singleByte;
self::$_map['(cork|t1)'] = $singleByte;
self::$_map['v?iscii'] = $singleByte;
//16 bits
self::$_map['(ucs-?2|utf-?16)'] = $doubleByte;
//32 bits
self::$_map['(ucs-?4|utf-?32)'] = $fourBytes;
// Fallback
self::$_map['.*'] = $singleByte;
}
/**
* Returns a CharacterReader suitable for the charset applied.
*
* @param string $charset
*
* @return Swift_CharacterReader
*/
public function getReaderFor($charset)
{
$charset = trim(strtolower($charset));
foreach (self::$_map as $pattern => $spec) {
$re = '/^' . $pattern . '$/D';
if (preg_match($re, $charset)) {
if (!array_key_exists($pattern, self::$_loaded)) {
$reflector = new ReflectionClass($spec['class']);
if ($reflector->getConstructor()) {
$reader = $reflector->newInstanceArgs($spec['constructor']);
} else {
$reader = $reflector->newInstance();
}
self::$_loaded[$pattern] = $reader;
}
return self::$_loaded[$pattern];
}
}
}
}

View file

@ -1,89 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An abstract means of reading and writing data in terms of characters as opposed
* to bytes.
*
* Classes implementing this interface may use a subsystem which requires less
* memory than working with large strings of data.
*
* @author Chris Corbyn
*/
interface Swift_CharacterStream
{
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset);
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory);
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os);
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string);
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length);
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return int[]
*/
public function readBytes($length);
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars);
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset);
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents();
}

View file

@ -1,294 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Chris Corbyn
*/
class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream
{
/** A map of byte values and their respective characters */
private static $_charMap;
/** A map of characters and their derivative byte values */
private static $_byteMap;
/** The char reader (lazy-loaded) for the current charset */
private $_charReader;
/** A factory for creating CharacterReader instances */
private $_charReaderFactory;
/** The character set this stream is using */
private $_charset;
/** Array of characters */
private $_array = array();
/** Size of the array of character */
private $_array_size = array();
/** The current character offset in the stream */
private $_offset = 0;
/**
* Create a new CharacterStream with the given $chars, if set.
*
* @param Swift_CharacterReaderFactory $factory for loading validators
* @param string $charset used in the stream
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
self::_initializeMaps();
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->_charset = $charset;
$this->_charReader = null;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->_charReaderFactory = $factory;
}
/**
* Overwrite this character stream using the byte sequence in the byte stream.
*
* @param Swift_OutputByteStream $os output stream to read from
*/
public function importByteStream(Swift_OutputByteStream $os)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory
->getReaderFor($this->_charset);
}
$startLength = $this->_charReader->getInitialByteSize();
while (false !== $bytes = $os->read($startLength)) {
$c = array();
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$c[] = self::$_byteMap[$bytes[$i]];
}
$size = count($c);
$need = $this->_charReader
->validateByteSequence($c, $size);
if ($need > 0 &&
false !== $bytes = $os->read($need))
{
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$c[] = self::$_byteMap[$bytes[$i]];
}
}
$this->_array[] = $c;
++$this->_array_size;
}
}
/**
* Import a string a bytes into this CharacterStream, overwriting any existing
* data in the stream.
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* Read $length characters from the stream and move the internal pointer
* $length further into the stream.
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_offset == $this->_array_size) {
return false;
}
// Don't use array slice
$arrays = array();
$end = $length + $this->_offset;
for ($i = $this->_offset; $i < $end; ++$i) {
if (!isset($this->_array[$i])) {
break;
}
$arrays[] = $this->_array[$i];
}
$this->_offset += $i - $this->_offset; // Limit function calls
$chars = false;
foreach ($arrays as $array) {
$chars .= implode('', array_map('chr', $array));
}
return $chars;
}
/**
* Read $length characters from the stream and return a 1-dimensional array
* containing there octet values.
*
* @param int $length
*
* @return integer[]
*/
public function readBytes($length)
{
if ($this->_offset == $this->_array_size) {
return false;
}
$arrays = array();
$end = $length + $this->_offset;
for ($i = $this->_offset; $i < $end; ++$i) {
if (!isset($this->_array[$i])) {
break;
}
$arrays[] = $this->_array[$i];
}
$this->_offset += ($i - $this->_offset); // Limit function calls
return call_user_func_array('array_merge', $arrays);
}
/**
* Write $chars to the end of the stream.
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory->getReaderFor(
$this->_charset);
}
$startLength = $this->_charReader->getInitialByteSize();
$fp = fopen('php://memory', 'w+b');
fwrite($fp, $chars);
unset($chars);
fseek($fp, 0, SEEK_SET);
$buffer = array(0);
$buf_pos = 1;
$buf_len = 1;
$has_datas = true;
do {
$bytes = array();
// Buffer Filing
if ($buf_len - $buf_pos < $startLength) {
$buf = array_splice($buffer, $buf_pos);
$new = $this->_reloadBuffer($fp, 100);
if ($new) {
$buffer = array_merge($buf, $new);
$buf_len = count($buffer);
$buf_pos = 0;
} else {
$has_datas = false;
}
}
if ($buf_len - $buf_pos > 0) {
$size = 0;
for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) {
++$size;
$bytes[] = $buffer[$buf_pos++];
}
$need = $this->_charReader->validateByteSequence(
$bytes, $size);
if ($need > 0) {
if ($buf_len - $buf_pos < $need) {
$new = $this->_reloadBuffer($fp, $need);
if ($new) {
$buffer = array_merge($buffer, $new);
$buf_len = count($buffer);
}
}
for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) {
$bytes[] = $buffer[$buf_pos++];
}
}
$this->_array[] = $bytes;
++$this->_array_size;
}
} while ($has_datas);
fclose($fp);
}
/**
* Move the internal pointer to $charOffset in the stream.
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($charOffset > $this->_array_size) {
$charOffset = $this->_array_size;
} elseif ($charOffset < 0) {
$charOffset = 0;
}
$this->_offset = $charOffset;
}
/**
* Empty the stream and reset the internal pointer.
*/
public function flushContents()
{
$this->_offset = 0;
$this->_array = array();
$this->_array_size = 0;
}
private function _reloadBuffer($fp, $len)
{
if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) {
$buf = array();
for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) {
$buf[] = self::$_byteMap[$bytes[$i]];
}
return $buf;
}
return false;
}
private static function _initializeMaps()
{
if (!isset(self::$_charMap)) {
self::$_charMap = array();
for ($byte = 0; $byte < 256; ++$byte) {
self::$_charMap[$byte] = chr($byte);
}
self::$_byteMap = array_flip(self::$_charMap);
}
}
}

View file

@ -1,275 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A CharacterStream implementation which stores characters in an internal array.
*
* @author Xavier De Cock <xdecock@gmail.com>
*/
class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream
{
/**
* The char reader (lazy-loaded) for the current charset.
*
* @var Swift_CharacterReader
*/
private $_charReader;
/**
* A factory for creating CharacterReader instances.
*
* @var Swift_CharacterReaderFactory
*/
private $_charReaderFactory;
/**
* The character set this stream is using.
*
* @var string
*/
private $_charset;
/**
* The data's stored as-is.
*
* @var string
*/
private $_datas = '';
/**
* Number of bytes in the stream
*
* @var int
*/
private $_datasSize = 0;
/**
* Map.
*
* @var mixed
*/
private $_map;
/**
* Map Type.
*
* @var int
*/
private $_mapType = 0;
/**
* Number of characters in the stream.
*
* @var int
*/
private $_charCount = 0;
/**
* Position in the stream.
*
* @var int
*/
private $_currentPos = 0;
/**
* Constructor.
*
* @param Swift_CharacterReaderFactory $factory
* @param string $charset
*/
public function __construct(Swift_CharacterReaderFactory $factory, $charset)
{
$this->setCharacterReaderFactory($factory);
$this->setCharacterSet($charset);
}
/* -- Changing parameters of the stream -- */
/**
* Set the character set used in this CharacterStream.
*
* @param string $charset
*/
public function setCharacterSet($charset)
{
$this->_charset = $charset;
$this->_charReader = null;
$this->_mapType = 0;
}
/**
* Set the CharacterReaderFactory for multi charset support.
*
* @param Swift_CharacterReaderFactory $factory
*/
public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
{
$this->_charReaderFactory = $factory;
}
/**
* @see Swift_CharacterStream::flushContents()
*/
public function flushContents()
{
$this->_datas = null;
$this->_map = null;
$this->_charCount = 0;
$this->_currentPos = 0;
$this->_datasSize = 0;
}
/**
* @see Swift_CharacterStream::importByteStream()
*
* @param Swift_OutputByteStream $os
*/
public function importByteStream(Swift_OutputByteStream $os)
{
$this->flushContents();
$blocks=512;
$os->setReadPointer(0);
while(false!==($read = $os->read($blocks)))
$this->write($read);
}
/**
* @see Swift_CharacterStream::importString()
*
* @param string $string
*/
public function importString($string)
{
$this->flushContents();
$this->write($string);
}
/**
* @see Swift_CharacterStream::read()
*
* @param int $length
*
* @return string
*/
public function read($length)
{
if ($this->_currentPos>=$this->_charCount) {
return false;
}
$ret=false;
$length = ($this->_currentPos+$length > $this->_charCount)
? $this->_charCount - $this->_currentPos
: $length;
switch ($this->_mapType) {
case Swift_CharacterReader::MAP_TYPE_FIXED_LEN:
$len = $length*$this->_map;
$ret = substr($this->_datas,
$this->_currentPos * $this->_map,
$len);
$this->_currentPos += $length;
break;
case Swift_CharacterReader::MAP_TYPE_INVALID:
$end = $this->_currentPos + $length;
$end = $end > $this->_charCount
?$this->_charCount
:$end;
$ret = '';
for (; $this->_currentPos < $length; ++$this->_currentPos) {
if (isset ($this->_map[$this->_currentPos])) {
$ret .= '?';
} else {
$ret .= $this->_datas[$this->_currentPos];
}
}
break;
case Swift_CharacterReader::MAP_TYPE_POSITIONS:
$end = $this->_currentPos + $length;
$end = $end > $this->_charCount
?$this->_charCount
:$end;
$ret = '';
$start = 0;
if ($this->_currentPos>0) {
$start = $this->_map['p'][$this->_currentPos-1];
}
$to = $start;
for (; $this->_currentPos < $end; ++$this->_currentPos) {
if (isset($this->_map['i'][$this->_currentPos])) {
$ret .= substr($this->_datas, $start, $to - $start).'?';
$start = $this->_map['p'][$this->_currentPos];
} else {
$to = $this->_map['p'][$this->_currentPos];
}
}
$ret .= substr($this->_datas, $start, $to - $start);
break;
}
return $ret;
}
/**
* @see Swift_CharacterStream::readBytes()
*
* @param int $length
*
* @return integer[]
*/
public function readBytes($length)
{
$read=$this->read($length);
if ($read!==false) {
$ret = array_map('ord', str_split($read, 1));
return $ret;
}
return false;
}
/**
* @see Swift_CharacterStream::setPointer()
*
* @param int $charOffset
*/
public function setPointer($charOffset)
{
if ($this->_charCount<$charOffset) {
$charOffset=$this->_charCount;
}
$this->_currentPos = $charOffset;
}
/**
* @see Swift_CharacterStream::write()
*
* @param string $chars
*/
public function write($chars)
{
if (!isset($this->_charReader)) {
$this->_charReader = $this->_charReaderFactory->getReaderFor(
$this->_charset);
$this->_map = array();
$this->_mapType = $this->_charReader->getMapType();
}
$ignored='';
$this->_datas .= $chars;
$this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored);
if ($ignored!==false) {
$this->_datasSize=strlen($this->_datas)-strlen($ignored);
} else {
$this->_datasSize=strlen($this->_datas);
}
}
}

View file

@ -1,63 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Base class for Spools (implements time and message limits).
*
* @author Fabien Potencier
*/
abstract class Swift_ConfigurableSpool implements Swift_Spool
{
/** The maximum number of messages to send per flush */
private $_message_limit;
/** The time limit per flush */
private $_time_limit;
/**
* Sets the maximum number of messages to send per flush.
*
* @param int $limit
*/
public function setMessageLimit($limit)
{
$this->_message_limit = (int) $limit;
}
/**
* Gets the maximum number of messages to send per flush.
*
* @return int The limit
*/
public function getMessageLimit()
{
return $this->_message_limit;
}
/**
* Sets the time limit (in seconds) per flush.
*
* @param int $limit The limit
*/
public function setTimeLimit($limit)
{
$this->_time_limit = (int) $limit;
}
/**
* Gets the time limit (in seconds) per flush.
*
* @return int The limit
*/
public function getTimeLimit()
{
return $this->_time_limit;
}
}

View file

@ -1,370 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Dependency Injection container.
*
* @author Chris Corbyn
*/
class Swift_DependencyContainer
{
/** Constant for literal value types */
const TYPE_VALUE = 0x0001;
/** Constant for new instance types */
const TYPE_INSTANCE = 0x0010;
/** Constant for shared instance types */
const TYPE_SHARED = 0x0100;
/** Constant for aliases */
const TYPE_ALIAS = 0x1000;
/** Singleton instance */
private static $_instance = null;
/** The data container */
private $_store = array();
/** The current endpoint in the data container */
private $_endPoint;
/**
* Constructor should not be used.
*
* Use {@link getInstance()} instead.
*/
public function __construct() { }
/**
* Returns a singleton of the DependencyContainer.
*
* @return Swift_DependencyContainer
*/
public static function getInstance()
{
if (!isset(self::$_instance)) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* List the names of all items stored in the Container.
*
* @return array
*/
public function listItems()
{
return array_keys($this->_store);
}
/**
* Test if an item is registered in this container with the given name.
*
* @see register()
*
* @param string $itemName
*
* @return bool
*/
public function has($itemName)
{
return array_key_exists($itemName, $this->_store)
&& isset($this->_store[$itemName]['lookupType']);
}
/**
* Lookup the item with the given $itemName.
*
* @see register()
*
* @param string $itemName
*
* @return mixed
*
* @throws Swift_DependencyException If the dependency is not found
*/
public function lookup($itemName)
{
if (!$this->has($itemName)) {
throw new Swift_DependencyException(
'Cannot lookup dependency "' . $itemName . '" since it is not registered.'
);
}
switch ($this->_store[$itemName]['lookupType']) {
case self::TYPE_ALIAS:
return $this->_createAlias($itemName);
case self::TYPE_VALUE:
return $this->_getValue($itemName);
case self::TYPE_INSTANCE:
return $this->_createNewInstance($itemName);
case self::TYPE_SHARED:
return $this->_createSharedInstance($itemName);
}
}
/**
* Create an array of arguments passed to the constructor of $itemName.
*
* @param string $itemName
*
* @return array
*/
public function createDependenciesFor($itemName)
{
$args = array();
if (isset($this->_store[$itemName]['args'])) {
$args = $this->_resolveArgs($this->_store[$itemName]['args']);
}
return $args;
}
/**
* Register a new dependency with $itemName.
*
* This method returns the current DependencyContainer instance because it
* requires the use of the fluid interface to set the specific details for the
* dependency.
* @see asNewInstanceOf(), asSharedInstanceOf(), asValue()
*
* @param string $itemName
*
* @return Swift_DependencyContainer
*/
public function register($itemName)
{
$this->_store[$itemName] = array();
$this->_endPoint =& $this->_store[$itemName];
return $this;
}
/**
* Specify the previously registered item as a literal value.
*
* {@link register()} must be called before this will work.
*
* @param mixed $value
*
* @return Swift_DependencyContainer
*/
public function asValue($value)
{
$endPoint =& $this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_VALUE;
$endPoint['value'] = $value;
return $this;
}
/**
* Specify the previously registered item as an alias of another item.
*
* @param string $lookup
*
* @return Swift_DependencyContainer
*/
public function asAliasOf($lookup)
{
$endPoint =& $this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_ALIAS;
$endPoint['ref'] = $lookup;
return $this;
}
/**
* Specify the previously registered item as a new instance of $className.
*
* {@link register()} must be called before this will work.
* Any arguments can be set with {@link withDependencies()},
* {@link addConstructorValue()} or {@link addConstructorLookup()}.
*
* @see withDependencies(), addConstructorValue(), addConstructorLookup()
*
* @param string $className
*
* @return Swift_DependencyContainer
*/
public function asNewInstanceOf($className)
{
$endPoint =& $this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_INSTANCE;
$endPoint['className'] = $className;
return $this;
}
/**
* Specify the previously registered item as a shared instance of $className.
*
* {@link register()} must be called before this will work.
*
* @param string $className
*
* @return Swift_DependencyContainer
*/
public function asSharedInstanceOf($className)
{
$endPoint =& $this->_getEndPoint();
$endPoint['lookupType'] = self::TYPE_SHARED;
$endPoint['className'] = $className;
return $this;
}
/**
* Specify a list of injected dependencies for the previously registered item.
*
* This method takes an array of lookup names.
*
* @see addConstructorValue(), addConstructorLookup()
*
* @param array $lookups
*
* @return Swift_DependencyContainer
*/
public function withDependencies(array $lookups)
{
$endPoint =& $this->_getEndPoint();
$endPoint['args'] = array();
foreach ($lookups as $lookup) {
$this->addConstructorLookup($lookup);
}
return $this;
}
/**
* Specify a literal (non looked up) value for the constructor of the
* previously registered item.
*
* @see withDependencies(), addConstructorLookup()
*
* @param mixed $value
*
* @return Swift_DependencyContainer
*/
public function addConstructorValue($value)
{
$endPoint =& $this->_getEndPoint();
if (!isset($endPoint['args'])) {
$endPoint['args'] = array();
}
$endPoint['args'][] = array('type' => 'value', 'item' => $value);
return $this;
}
/**
* Specify a dependency lookup for the constructor of the previously
* registered item.
*
* @see withDependencies(), addConstructorValue()
*
* @param string $lookup
*
* @return Swift_DependencyContainer
*/
public function addConstructorLookup($lookup)
{
$endPoint =& $this->_getEndPoint();
if (!isset($this->_endPoint['args'])) {
$endPoint['args'] = array();
}
$endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup);
return $this;
}
/** Get the literal value with $itemName */
private function _getValue($itemName)
{
return $this->_store[$itemName]['value'];
}
/** Resolve an alias to another item */
private function _createAlias($itemName)
{
return $this->lookup($this->_store[$itemName]['ref']);
}
/** Create a fresh instance of $itemName */
private function _createNewInstance($itemName)
{
$reflector = new ReflectionClass($this->_store[$itemName]['className']);
if ($reflector->getConstructor()) {
return $reflector->newInstanceArgs(
$this->createDependenciesFor($itemName)
);
} else {
return $reflector->newInstance();
}
}
/** Create and register a shared instance of $itemName */
private function _createSharedInstance($itemName)
{
if (!isset($this->_store[$itemName]['instance'])) {
$this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName);
}
return $this->_store[$itemName]['instance'];
}
/** Get the current endpoint in the store */
private function &_getEndPoint()
{
if (!isset($this->_endPoint)) {
throw new BadMethodCallException(
'Component must first be registered by calling register()'
);
}
return $this->_endPoint;
}
/** Get an argument list with dependencies resolved */
private function _resolveArgs(array $args)
{
$resolved = array();
foreach ($args as $argDefinition) {
switch ($argDefinition['type']) {
case 'lookup':
$resolved[] = $this->_lookupRecursive($argDefinition['item']);
break;
case 'value':
$resolved[] = $argDefinition['item'];
break;
}
}
return $resolved;
}
/** Resolve a single dependency with an collections */
private function _lookupRecursive($item)
{
if (is_array($item)) {
$collection = array();
foreach ($item as $k => $v) {
$collection[$k] = $this->_lookupRecursive($v);
}
return $collection;
} else {
return $this->lookup($item);
}
}
}

View file

@ -1,27 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* DependencyException gets thrown when a requested dependency is missing.
*
* @author Chris Corbyn
*/
class Swift_DependencyException extends Swift_SwiftException
{
/**
* Create a new DependencyException with $message.
*
* @param string $message
*/
public function __construct($message)
{
parent::__construct($message);
}
}

View file

@ -1,69 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An embedded file, in a multipart message.
*
* @author Chris Corbyn
*/
class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile
{
/**
* Create a new EmbeddedFile.
*
* Details may be optionally provided to the constructor.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*/
public function __construct($data = null, $filename = null, $contentType = null)
{
call_user_func_array(
array($this, 'Swift_Mime_EmbeddedFile::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('mime.embeddedfile')
);
$this->setBody($data);
$this->setFilename($filename);
if ($contentType) {
$this->setContentType($contentType);
}
}
/**
* Create a new EmbeddedFile.
*
* @param string|Swift_OutputByteStream $data
* @param string $filename
* @param string $contentType
*
* @return Swift_Mime_EmbeddedFile
*/
public static function newInstance($data = null, $filename = null, $contentType = null)
{
return new self($data, $filename, $contentType);
}
/**
* Create a new EmbeddedFile from a filesystem path.
*
* @param string $path
*
* @return Swift_Mime_EmbeddedFile
*/
public static function fromPath($path)
{
return self::newInstance()->setFile(
new Swift_ByteStream_FileByteStream($path)
);
}
}

View file

@ -1,27 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for all Encoder schemes.
* @author Chris Corbyn
*/
interface Swift_Encoder extends Swift_Mime_CharsetObserver
{
/**
* Encode a given string to produce an encoded string.
*
* @param string $string
* @param int $firstLineOffset if first line needs to be shorter
* @param int $maxLineLength - 0 indicates the default length for this encoding
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0);
}

View file

@ -1,58 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Base 64 Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Base64Encoder implements Swift_Encoder
{
/**
* Takes an unencoded string and produces a Base64 encoded string from it.
*
* Base64 encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 76 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if (0 >= $maxLineLength || 76 < $maxLineLength) {
$maxLineLength = 76;
}
$encodedString = base64_encode($string);
$firstLine = '';
if (0 != $firstLineOffset) {
$firstLine = substr(
$encodedString, 0, $maxLineLength - $firstLineOffset
) . "\r\n";
$encodedString = substr(
$encodedString, $maxLineLength - $firstLineOffset
);
}
return $firstLine . trim(chunk_split($encodedString, $maxLineLength, "\r\n"));
}
/**
* Does nothing.
*/
public function charsetChanged($charset)
{
}
}

View file

@ -1,282 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles Quoted Printable (QP) Encoding in Swift Mailer.
*
* Possibly the most accurate RFC 2045 QP implementation found in PHP.
*
* @author Chris Corbyn
*/
class Swift_Encoder_QpEncoder implements Swift_Encoder
{
/**
* The CharacterStream used for reading characters (as opposed to bytes).
*
* @var Swift_CharacterStream
*/
protected $_charStream;
/**
* A filter used if input should be canonicalized.
*
* @var Swift_StreamFilter
*/
protected $_filter;
/**
* Pre-computed QP for HUGE optimization.
*
* @var string[]
*/
protected static $_qpMap = array(
0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04',
5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09',
10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E',
15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13',
20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18',
25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D',
30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22',
35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27',
40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C',
45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31',
50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36',
55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B',
60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40',
65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45',
70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A',
75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F',
80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54',
85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59',
90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E',
95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63',
100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68',
105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D',
110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72',
115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77',
120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C',
125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81',
130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86',
135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B',
140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90',
145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95',
150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A',
155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F',
160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4',
165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9',
170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE',
175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3',
180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8',
185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD',
190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2',
195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7',
200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC',
205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1',
210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6',
215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB',
220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0',
225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5',
230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA',
235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF',
240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4',
245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9',
250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE',
255 => '=FF'
);
protected static $_safeMapShare = array();
/**
* A map of non-encoded ascii characters.
*
* @var string[]
*/
protected $_safeMap = array();
/**
* Creates a new QpEncoder for the given CharacterStream.
*
* @param Swift_CharacterStream $charStream to use for reading characters
* @param Swift_StreamFilter $filter if input should be canonicalized
*/
public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null)
{
$this->_charStream = $charStream;
if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) {
$this->initSafeMap();
self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
} else {
$this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
}
$this->_filter = $filter;
}
public function __sleep()
{
return array('_charStream', '_filter');
}
public function __wakeup()
{
if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) {
$this->initSafeMap();
self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
} else {
$this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
}
}
protected function getSafeMapShareId()
{
return get_class($this);
}
protected function initSafeMap()
{
foreach (array_merge(
array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte)
{
$this->_safeMap[$byte] = chr($byte);
}
}
/**
* Takes an unencoded string and produces a QP encoded string from it.
*
* QP encoded strings have a maximum line length of 76 characters.
* If the first line needs to be shorter, indicate the difference with
* $firstLineOffset.
*
* @param string $string to encode
* @param int $firstLineOffset, optional
* @param int $maxLineLength, optional 0 indicates the default of 76 chars
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
if ($maxLineLength > 76 || $maxLineLength <= 0) {
$maxLineLength = 76;
}
$thisLineLength = $maxLineLength - $firstLineOffset;
$lines = array();
$lNo = 0;
$lines[$lNo] = '';
$currentLine =& $lines[$lNo++];
$size=$lineLen=0;
$this->_charStream->flushContents();
$this->_charStream->importString($string);
// Fetching more than 4 chars at one is slower, as is fetching fewer bytes
// Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6
// bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes
while (false !== $bytes = $this->_nextSequence()) {
// If we're filtering the input
if (isset($this->_filter)) {
// If we can't filter because we need more bytes
while ($this->_filter->shouldBuffer($bytes)) {
// Then collect bytes into the buffer
if (false === $moreBytes = $this->_nextSequence(1)) {
break;
}
foreach ($moreBytes as $b) {
$bytes[] = $b;
}
}
// And filter them
$bytes = $this->_filter->filter($bytes);
}
$enc = $this->_encodeByteSequence($bytes, $size);
if ($currentLine && $lineLen+$size >= $thisLineLength) {
$lines[$lNo] = '';
$currentLine =& $lines[$lNo++];
$thisLineLength = $maxLineLength;
$lineLen=0;
}
$lineLen+=$size;
$currentLine .= $enc;
}
return $this->_standardize(implode("=\r\n", $lines));
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->_charStream->setCharacterSet($charset);
}
/**
* Encode the given byte array into a verbatim QP form.
*
* @param integer[] $bytes
* @param int $size
*
* @return string
*/
protected function _encodeByteSequence(array $bytes, &$size)
{
$ret = '';
$size=0;
foreach ($bytes as $b) {
if (isset($this->_safeMap[$b])) {
$ret .= $this->_safeMap[$b];
++$size;
} else {
$ret .= self::$_qpMap[$b];
$size+=3;
}
}
return $ret;
}
/**
* Get the next sequence of bytes to read from the char stream.
*
* @param int $size number of bytes to read
*
* @return integer[]
*/
protected function _nextSequence($size = 4)
{
return $this->_charStream->readBytes($size);
}
/**
* Make sure CRLF is correct and HT/SPACE are in valid places.
*
* @param string $string
*
* @return string
*/
protected function _standardize($string)
{
$string = str_replace(array("\t=0D=0A", " =0D=0A", "=0D=0A"),
array("=09\r\n", "=20\r\n", "\r\n"), $string
);
switch ($end = ord(substr($string, -1))) {
case 0x09:
case 0x20:
$string = substr_replace($string, self::$_qpMap[$end], -1);
}
return $string;
}
}

View file

@ -1,84 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles RFC 2231 specified Encoding in Swift Mailer.
*
* @author Chris Corbyn
*/
class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder
{
/**
* A character stream to use when reading a string as characters instead of bytes.
*
* @var Swift_CharacterStream
*/
private $_charStream;
/**
* Creates a new Rfc2231Encoder using the given character stream instance.
*
* @param Swift_CharacterStream
*/
public function __construct(Swift_CharacterStream $charStream)
{
$this->_charStream = $charStream;
}
/**
* Takes an unencoded string and produces a string encoded according to
* RFC 2231 from it.
*
* @param string $string
* @param int $firstLineOffset
* @param int $maxLineLength optional, 0 indicates the default of 75 bytes
*
* @return string
*/
public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
{
$lines = array(); $lineCount = 0;
$lines[] = '';
$currentLine =& $lines[$lineCount++];
if (0 >= $maxLineLength) {
$maxLineLength = 75;
}
$this->_charStream->flushContents();
$this->_charStream->importString($string);
$thisLineLength = $maxLineLength - $firstLineOffset;
while (false !== $char = $this->_charStream->read(4)) {
$encodedChar = rawurlencode($char);
if (0 != strlen($currentLine)
&& strlen($currentLine . $encodedChar) > $thisLineLength)
{
$lines[] = '';
$currentLine =& $lines[$lineCount++];
$thisLineLength = $maxLineLength;
}
$currentLine .= $encodedChar;
}
return implode("\r\n", $lines);
}
/**
* Updates the charset used.
*
* @param string $charset
*/
public function charsetChanged($charset)
{
$this->_charStream->setCharacterSet($charset);
}
}

View file

@ -1,64 +0,0 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Provides quick access to each encoding type.
*
* @author Chris Corbyn
*/
class Swift_Encoding
{
/**
* Get the Encoder that provides 7-bit encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function get7BitEncoding()
{
return self::_lookup('mime.7bitcontentencoder');
}
/**
* Get the Encoder that provides 8-bit encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function get8BitEncoding()
{
return self::_lookup('mime.8bitcontentencoder');
}
/**
* Get the Encoder that provides Quoted-Printable (QP) encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function getQpEncoding()
{
return self::_lookup('mime.qpcontentencoder');
}
/**
* Get the Encoder that provides Base64 encoding.
*
* @return Swift_Mime_ContentEncoder
*/
public static function getBase64Encoding()
{
return self::_lookup('mime.base64contentencoder');
}
// -- Private Static Methods
private static function _lookup($key)
{
return Swift_DependencyContainer::getInstance()->lookup($key);
}
}

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