text:
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2020 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

package com.google.appinventor.components.runtime.util;

import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

import android.annotation.SuppressLint;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;

import android.content.res.AssetManager;

import android.database.Cursor;

import android.net.Uri;

import android.os.Build;
import android.os.Environment;

import android.provider.MediaStore;

import android.util.Log;

import com.google.appinventor.components.common.FileScope;

import com.google.appinventor.components.runtime.Form;

import com.google.appinventor.components.runtime.errors.PermissionException;
import com.google.appinventor.components.runtime.errors.RuntimeError;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.net.URI;
import java.net.URL;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Utilities for reading and writing files to the external storage.
 *
 * @author lizlooney@google.com (Liz Looney)
 */
@SuppressLint("InlinedApi")
public class FileUtil {
  /**
   * The minimum SDK version for which we enforce the use of app-specific directories on the
   * external storage. Starting with Android Q, apps cannot write to arbitrary locations on
   * external storage unless a specific attribute is set in the manifest. Starting with Android R,
   * this will only be allowed if the app is installed prior to the device being upgraded to
   * Android R and only if the app continues to be upgraded. A fresh install wipes the privileges
   * and apps must write to the app-specific directory.
   *
   *
This must be 8 or more. App-specific directories are not supported on versions of Android
   * before SDK 8 (2.2 Froyo).
   */
  public static final int MIN_SDK_FOR_APP_SPECIFIC_DIRS = Build.VERSION_CODES.Q;

  private static final String LOG_TAG = FileUtil.class.getSimpleName();
  // Note: Some phones come with a "Documents" directory rather than a
  // "My Documents" directory.  Should we check for this and try to be
  // consistent with the phone's directory structure or to be consistent
  // in directory names across phones?
  private static final String DOCUMENT_DIRECTORY = "My Documents/";

  private static final String DIRECTORY_RECORDINGS = "Recordings";

  private static final String FILENAME_PREFIX = "app_inventor_";

  // This has the same value as Environment.DIRECTORY_PICTURES, which was
  // not defined until API Level 8 (Android 2.2).
  // If we use Environment.DIRECTORY_PICTURES here, then this class (FileUtil)
  // will be rejected by the dalvikvm class verifier on older versions of
  // Android.
  private static final String DIRECTORY_PICTURES = "Pictures";

  private static final String DIRECTORY_DOWNLOADS = "Downloads";

  static {
    //noinspection ConstantConditions
    if (MIN_SDK_FOR_APP_SPECIFIC_DIRS < Build.VERSION_CODES.FROYO) {
      throw new IllegalStateException("MIN_SDK_FOR_APP_SPECIFIC_DIRS must be 8 or greater");
    }
  }

  private FileUtil() {
  }

  /**
   * Returns an URL for the given local file.
   */
  public static String getFileUrl(String localFileName) {
    File file = new File(localFileName);
    return file.toURI().toString();
  }

  /**
   * Reads the given local file and returns the contents as a byte array.
   *
   *
This function is deprecated. Developers should use
   * {@link #readFile(Form, String)} instead.

   *
   * @param inputFileName the name of the file to read from
   * @return the file's contents as a byte array
   */
  @Deprecated
  public static byte[] readFile(String inputFileName) throws IOException {
    Log.w(LOG_TAG, "Calling deprecated function readFile", new IllegalAccessException());
    return readFile(Form.getActiveForm(), inputFileName);
  }

  /**
   * Reads the given local file and returns the contents as a byte array.
   *
   * @param form the form to use as an Android context
   * @param inputFileName the name of the file to read from
   * @return the file's contents as a byte array
   */
  public static byte[] readFile(Form form, String inputFileName) throws IOException {
    File inputFile = new File(inputFileName);
    // There are cases where our caller will hand us a file to read that
    // doesn't exist and is expecting a FileNotFoundException if this is the
    // case. So we check if the file exists and throw the exception
    if (!inputFile.isFile()) {
      throw new FileNotFoundException("Cannot find file: " + inputFileName);
    }
    FileInputStream inputStream = null;
    byte[] content;
    try {
      inputStream = openFile(form, inputFileName);
      int fileLength = (int) inputFile.length();
      content = new byte[fileLength];
      int offset = 0;
      int bytesRead;
      do {
        bytesRead = inputStream.read(content, offset, fileLength - offset);
        if (bytesRead > 0) {
          offset += bytesRead;
        }
        if (offset == fileLength) {
          break;
        }
      } while (bytesRead >= 0);
    } finally {
      if (inputStream != null) {
        inputStream.close();
      }
    }
    return content;
  }

  /**
   * Opens the file at the given file name.
   *
   *

   * If the file is an external file and the app is running on a version of Android at SDK level 23
   * or higher, then the READ_EXTERNAL_STORAGE permission will be checked. If the app does not have
   * permission to read the external file, a PermissionException will be thrown.
   *

   *
   *

   * This function is deprecated. Developers should use {@link #openFile(Form, String)} instead.
   *

   *
   * @param fileName The file path to open.
   * @return An open FileInputStream on success
   * @throws IOException If the file cannot be opened
   * @throws PermissionException If the app does not have permission to read external files and the
   *     pathname looks to be external.
   */
  @Deprecated
  public static FileInputStream openFile(String fileName) throws IOException, PermissionException {
    Log.w(LOG_TAG, "Calling deprecated function openFile", new IllegalAccessException());
    return openFile(Form.getActiveForm(), fileName);
  }

  /**
   * Opens the file at the given file name.
   *
   *

   * If the file is an external file and the app is running on a version of Android at SDK level 23
   * or higher, then the READ_EXTERNAL_STORAGE permission will be checked. If the app does not have
   * permission to read the external file, a PermissionException will be thrown.
   *

   *
   * @param form the form to use as an Android context
   * @param fileName The file path to open.
   * @return An open FileInputStream on success
   * @throws IOException If the file cannot be opened
   * @throws PermissionException If the app does not have permission to read external files and the
   *     pathname looks to be external.
   */
  public static FileInputStream openFile(Form form, String fileName) throws IOException,
      PermissionException {
    if (MediaUtil.isExternalFile(form, fileName)) {
      form.assertPermission(READ_EXTERNAL_STORAGE);
    }
    return new FileInputStream(fileName);
  }

  /**
   * Opens the file identified by the given File object.
   *
   *

   * If the file is an external file and the app is running on a version of Android at SDK level 23
   * or higher, then the READ_EXTERNAL_STORAGE permission will be checked. If the app does not have
   * permission to read the external file, a PermissionException will be thrown.
   *

   *
   *

   * This function is deprecated. Developers should use {@link #openFile(Form, File)} instead.
   *

   *
   * @param file The file to open.
   * @return An open FileInputStream on success
   * @throws IOException If the file cannot be opened
   * @throws PermissionException If the app does not have permission to read external files and the
   *     pathname looks to be external.
   */
  @Deprecated
  public static FileInputStream openFile(File file) throws IOException, PermissionException {
    Log.w(LOG_TAG, "Calling deprecated function openFile", new IllegalAccessException());
    return openFile(Form.getActiveForm(), file.getAbsolutePath());
  }

  /**
   * Opens the file identified by the given File object.
   *
   *

   * If the file is an external file and the app is running on a version of Android at SDK level 23
   * or higher, then the READ_EXTERNAL_STORAGE permission will be checked. If the app does not have
   * permission to read the external file, a PermissionException will be thrown.
   *

   *
   * @param form the form to use as an Android context
   * @param file The file to open.
   * @return An open FileInputStream on success
   * @throws IOException If the file cannot be opened
   * @throws PermissionException If the app does not have permission to read external files and the
   *     pathname looks to be external.
   */
  public static FileInputStream openFile(Form form, File file) throws IOException,
      PermissionException {
    return openFile(form, file.getAbsolutePath());
  }

  /**
   * Opens the file at the given file URI.
   *
   *

   * If the file is an external file and the app is running on a version of Android at SDK level 23
   * or higher, then the READ_EXTERNAL_STORAGE permission will be checked. If the app does not have
   * permission to read the external file, a PermissionException will be thrown.
   *

   *
   *

   * This function is deprecated. Developers should use {@link #openFile(Form, URI)} instead.
   *

   *
   * @param fileUri The file URI to open.
   * @return An open FileInputStream on success
   * @throws IOException If the file cannot be opened
   * @throws PermissionException If the app does not have permission to read external files and the
   *     pathname looks to be external.
   */
  @Deprecated
  public static FileInputStream openFile(URI fileUri) throws IOException, PermissionException {
    Log.w(LOG_TAG, "Calling deprecated function openFile", new IllegalAccessException());
    return openFile(Form.getActiveForm(), fileUri);
  }

  /**
   * Opens the file at the given file URI.
   *
   *

   * If the file is an external file and the app is running on a version of Android at SDK level 23
   * or higher, then the READ_EXTERNAL_STORAGE permission will be checked. If the app does not have
   * permission to read the external file, a PermissionException will be thrown.
   *

   *
   * @param form the form to use as an Android context
   * @param fileUri The file URI to open.
   * @return An open FileInputStream on success
   * @throws IOException If the file cannot be opened
   * @throws PermissionException If the app does not have permission to read external files and the
   *     pathname looks to be external.
   */
  public static FileInputStream openFile(Form form, URI fileUri) throws IOException,
      PermissionException {
    if (needsPermission(form, fileUri.toString())) {
      form.assertPermission(READ_EXTERNAL_STORAGE);
    }
    return new FileInputStream(new File(fileUri));
  }

  /**
   * Downloads the resource with the given URL and writes it as a local file.
   *
   * @param url the URL to read from
   * @param outputFileName the name of the file to write to
   * @return the URL for the local file
   */
  public static String downloadUrlToFile(String url, String outputFileName) throws IOException {
    InputStream in = new URL(url).openStream();
    try {
      return writeStreamToFile(in, outputFileName);
    } finally {
      in.close();
    }
  }

  /**
   * Writes the given byte array as a local file.
   *
   * @param array the byte array to read from
   * @param outputFileName the name of the file to write to
   * @return the URL for the local file
   */
  public static String writeFile(byte[] array, String outputFileName) throws IOException {
    InputStream in = new ByteArrayInputStream(array);
    try {
      return writeStreamToFile(in, outputFileName);
    } finally {
      in.close();
    }
  }

  /**
   * Copies the contents of one local file to another local file.
   *
   * @param inputFileName the name of the file to read to
   * @param outputFileName the name of the file to write to
   * @return the URL for the local file
   */
  public static String copyFile(String inputFileName, String outputFileName)
      throws IOException {
    InputStream in = new FileInputStream(inputFileName);
    try {
      return writeStreamToFile(in, outputFileName);
    } finally {
      in.close();
    }
  }

  /**
   * Copy a file from one scope to another.
   *
   * @param form the form to serve as a context for Android operations
   * @param src the source file
   * @param dest the destination file
   * @return true if the file was copied successfully, false otherwise
   * @throws IOException if an I/O exception occurs while copying the original file
   */
  public static boolean copyFile(Form form, ScopedFile src, ScopedFile dest) throws IOException {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
        && src.getScope() != FileScope.Shared && dest.getScope() != FileScope.Shared) {
      // Because Shared files go through the MediaStore, we cannot use the NIO API for copying.
      // New style. Use Java NIO to move the file, potentially between file system providers
      Files.copy(Paths.get(src.resolve(form).toURI()), Paths.get(dest.resolve(form).toURI()),
          REPLACE_EXISTING);
    } else {
      InputStream in = null;
      OutputStream out = null;
      try {
        in = openForReading(form, src);
        out = openForWriting(form, src);
        byte[] buffer = new byte[4096];
        int read;
        while ((read = in.read(buffer)) > 0) {
          out.write(buffer, 0, read);
        }
      } finally {
        IOUtils.closeQuietly(LOG_TAG, in);
        IOUtils.closeQuietly(LOG_TAG, out);
      }
    }
    return true;
  }

  /**
   * Writes the contents from the given input stream to the given file.
   *
   * @param in the InputStream to read from
   * @param outputFileName the name of the file to write to
   * @return the URL for the local file
   */
  public static String writeStreamToFile(InputStream in, String outputFileName) throws IOException {
    File file = new File(outputFileName);

    // Create the parent directory.
    file.getParentFile().mkdirs();

    OutputStream out = new FileOutputStream(file);
    try {
      copy(in, out);

      // Return the URL to the output file.
      return file.toURI().toString();
    } finally {
      out.flush();
      out.close();
    }
  }

  /**
   * Copy the contents of the input stream {@code in} to the output stream {@code out}.
   *
   * @param in the stream to read
   * @param out the stream to write
   * @throws IOException when the stream(s) cannot be accessed
   */
  public static void copy(InputStream in, OutputStream out) throws IOException {
    out = new BufferedOutputStream(out, 0x1000);
    in = new BufferedInputStream(in, 0x1000);

    // Copy the contents from the input stream to the output stream.
    while (true) {
      int b = in.read();
      if (b == -1) {
        break;
      }
      out.write(b);
    }
    out.flush();
  }

  /**
   * Creates a {@link File} representing the complete path for an image
   * file, creating the enclosing directories if needed.  This does not actually
   * open the file.  Any component that calls this must have
   * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
   *
   *

   * This function is deprecrated. Developers should use
   * {@link #getPictureFile(Form, String)} instead.
   *

   *
   * @param extension file extension, such as "png" or "jpg"
   * @return the path to the file
   * @throws IOException if the enclosing directory cannot be created
   * @throws FileException if external storage is not accessible or not writable
   *     with the appropriate ErrorMessages error code.
   */
  @Deprecated
  public static File getPictureFile(String extension)
      throws IOException, FileException {
    Log.w(LOG_TAG, "Calling deprecated function getPictureFile", new IllegalAccessException());
    return getPictureFile(Form.getActiveForm(), extension);
  }

  /**
   * Creates a {@link File} representing the complete path for an image
   * file, creating the enclosing directories if needed.  This does not actually
   * open the file.  Any component that calls this must have
   * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
   *
   * @param form the form to use as an Android context
   * @param extension file extension, such as "png" or "jpg"
   * @return the path to the file
   * @throws IOException if the enclosing directory cannot be created
   * @throws FileException if external storage is not accessible or not writable
   *     with the appropriate ErrorMessages error code.
   */
  public static File getPictureFile(Form form, String extension)
      throws IOException, FileException {
    return getFile(form, DIRECTORY_PICTURES, extension);
  }

  /**
   * Creates a {@link ScopedFile} for a picture file with the given {@code extension} relative to
   * the Picture directory in the {@code form}'s {@link Form#DefaultFileScope()}.
   *
   * @param form the Form object to provide the scope
   * @param extension the image extension (e.g., "png")
   * @return a fresh file, scoped to the form's default scope
   */
  public static ScopedFile getScopedPictureFile(Form form, String extension) {
    return getScopedFile(form, DIRECTORY_PICTURES, extension);
  }

  /**
   * Creates a {@link File} representing the complete path for a recording,
   * creating the enclosing directories if needed.  This does not actually
   * open the file.  Any component that calls this must have
   * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
   *
   *

   * This function is deprecated. Developers should use
   * {@link #getRecordingFile(Form, String)} instead.
   *

   *
   * @return the path to the file
   * @param extension file extension, such as "3gp"
   * @throws IOException if the enclosing directory cannot be created
   * @throws FileException if external storage is not accessible or not writable
   *     with the appropriate ErrorMessages error code.
   */
  @Deprecated
  public static File getRecordingFile(String extension)
      throws IOException, FileException {
    return getRecordingFile(Form.getActiveForm(), extension);
  }

  /**
   * Creates a {@link File} representing the complete path for a recording,
   * creating the enclosing directories if needed.  This does not actually
   * open the file.  Any component that calls this must have
   * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
   *
   * @param form the form to use as an Android context
   * @param extension file extension, such as "3gp"
   * @return the path to the file
   * @throws IOException if the enclosing directory cannot be created
   * @throws FileException if external storage is not accessible or not writable
   *     with the appropriate ErrorMessages error code.
   */
  public static File getRecordingFile(Form form, String extension)
      throws IOException, FileException {
    return getFile(form, DIRECTORY_RECORDINGS, extension);
  }

  /**
   * Creates a {@link File} representing the complete path for a downloaded file,
   * creating the enclosing directories if needed.  This does not actually
   * open the file.  Any component that calls this must have
   * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
   *
   *

   * This function is deprecated. Developers should use
   * {@link #getDownloadFile(Form, String)} instead.
   *

   *
   * @return the path to the file
   * @param extension file extension, such as "tmp"
   * @throws IOException if the enclosing directory cannot be created
   * @throws FileException if external storage is not accessible or not writable
   *     with the appropriate ErrorMessages error code.
   */
  @Deprecated
  public static File getDownloadFile(String extension)
      throws IOException, FileException {
    Log.w(LOG_TAG, "Calling deprecated function getDownloadFile", new IllegalAccessException());
    return getDownloadFile(Form.getActiveForm(), extension);
  }

  /**
   * Creates a {@link File} representing the complete path for a downloaded file,
   * creating the enclosing directories if needed.  This does not actually
   * open the file.  Any component that calls this must have
   * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
   *
   * @param form the form to use as an Android context
   * @param extension file extension, such as "tmp"
   * @return the path to the file
   * @throws IOException if the enclosing directory cannot be created
   * @throws FileException if external storage is not accessible or not writable
   *     with the appropriate ErrorMessages error code.
   */
  public static File getDownloadFile(Form form, String extension)
      throws IOException, FileException {
    return getFile(form, DIRECTORY_DOWNLOADS, extension);
  }

  /**
   * Determines the best directory in which to store a file of the given type
   * and creates the directory if it does not exist, generating a full path.
   *
   * @param category a descriptive category, such as {@link #DIRECTORY_PICTURES}
   *        to include in the path
   * @param extension the extension for the end of the file, not including the
   *        period, such as "png"
   * @return the full path to the file
   * @throws IOException if the directory cannot be created
   */
  private static File getFile(Form form, String category, String extension)
      throws IOException, FileException {
    String fileName = DOCUMENT_DIRECTORY + category + "/" + FILENAME_PREFIX
        + System.currentTimeMillis() + "." + extension;
    return getExternalFile(form, fileName);
  }

  /**
   * Determines the best directory in which to store a file of the given type
   * and generates the corresponding ScopedFile object using the default scope
   * provided by the {@code form}. Note that since {@link FileScope#Asset} scope
   * is read-only, calls to this method will use {@link FileScope#Private} as
   * a fallback.
   *
   * @param form the form to use as an Android context
   * @param category a descriptive category, such as {@link #DIRECTORY_PICTURES}
   *                 to include in the path
   * @param extension the extension for the end of the file, not including the
   *                  period, such as "png"
   * @return the ScopedFile referencing
   */
  private static ScopedFile getScopedFile(Form form, String category, String extension) {
    String fullPath;
    FileScope scope = form.DefaultFileScope();
    if (scope == FileScope.Legacy) {
      // NB: we need the first slash here when using Legacy mode so that the file will appear in the
      // external storage rather than internal storage.
      fullPath = "/" + DOCUMENT_DIRECTORY + category;
    } else {
      fullPath = category;
      if (scope == FileScope.Asset) {
        // This method is called to create new files, but assets are read-only.
        // We fall back to private storage in this case
        scope = FileScope.Private;
      }
    }
    fullPath += "/" + FILENAME_PREFIX + System.currentTimeMillis() + "." + extension;
    return new ScopedFile(scope, fullPath);
  }

  /**
   * Returns the File for fileName in the external storage directory in
   * preparation for writing the file. fileName may contain sub-directories.
   * Ensures that all subdirectories exist and that fileName does not exist
   * (deleting it if necessary).
   *
   *

   * This function is deprecated. Developers should use
   * {@link #getExternalFile(Form, String)} instead.
   *

   *
   * @param fileName The path name of the file relative to the external storage
   *     directory
   * @return the File object for creating fileName in the external storage
   * @throws IOException if we are unable to create necessary parent directories
   *     or delete an existing file
   * @throws FileException if the external storage is not writeable.
   */
  @Deprecated
  public static File getExternalFile(String fileName) throws IOException, FileException,
      SecurityException {
    return getExternalFile(Form.getActiveForm(), fileName);
  }

  /**
   * Returns the File for fileName in the external storage directory in
   * preparation for writing the file. fileName may contain sub-directories.
   *
   * @param form the form to use as an Android context
   * @param fileName The path name of the file relative to the external storage
   *     directory
   * @return the File object for creating fileName in the external storage
   * @throws FileException if the external storage is not writeable.
   */
  public static File getExternalFile(Form form, String fileName) throws FileException {
    if (form.DefaultFileScope() == FileScope.Legacy && !fileName.startsWith("/")) {
      // The legacy File component used a single slash as a prefix to indicate external storage.
      fileName = "/" + fileName;
    }
    String uri = resolveFileName(form, fileName, form.DefaultFileScope());
    if (isExternalStorageUri(form, uri)) {
      checkExternalStorageWriteable();
    }
    if (needsPermission(form, uri)) {
      form.assertPermission(WRITE_EXTERNAL_STORAGE);
    }
    return new File(URI.create(uri));
  }

  /**
   * Returns the File for fileName in the external storage directory in
   * preparation for writing the file. fileName may contain sub-directories.
   * Ensures that all subdirectories exist and that fileName does not exist
   * (deleting it if necessary).
   *
   * @param form the form to use as an Android context
   * @param fileName The path name of the file relative to the external storage
   *     directory
   * @param mkdirs true if the ancestor directories should be created, otherwise false
   * @param overwrite true if the file is going to be overwritten, otherwise false
   * @return the File object for creating fileName in the external storage
   * @throws IOException if we are unable to create necessary parent directories
   *     or delete an existing file
   * @throws FileException if the external storage is not writeable.
   */
  public static File getExternalFile(Form form, String fileName, boolean mkdirs, boolean overwrite)
      throws IOException, FileException {
    File file = getExternalFile(form, fileName);
    File directory = file.getParentFile();
    if (mkdirs && !directory.exists() && !directory.mkdirs()) {
      throw new IOException("Unable to create directory " + directory.getAbsolutePath());
    }
    if (overwrite && file.exists() && !file.delete()) {
      throw new IOException("Cannot overwrite existing file " + file.getAbsolutePath());
    }
    return file;
  }

  /**
   * Gets an external file {@code fileName} on the external storage.
   *
   *
The {@code form} is used as a context for asking for permissions and generating an
   * app-specific directory based on the {@code accessMode} provided.
   *
   * @param form The form to use as the context for the operation(s)
   * @param fileName The filename to read/write/append
   * @param scope The permission model to apply to determine the file location
   * @return The File representing the filename on the external storage. The exact location depends
   *     on the value of {@code accessMode}.
   * @throws FileException if the external storage is not writeable
   * @throws PermissionException if the app doesn't have the necessary permissions to write the file
   */
  public static File getExternalFile(Form form, String fileName, FileScope scope)
      throws FileException, PermissionException {
    return new File(URI.create(resolveFileName(form, fileName, scope)));
  }

  /**
   * Gets an external file {@code fileName} on the external storage.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileName the name of the file to be accessed, using the File semantics
   * @param scope permission mode to use for locating the file and asking permissions
   * @param accessMode the direction of the access (read, write, append)
   * @param mkdirs true if any ancestor directories should be made if they don't exist
   * @return a new File object representing the external file
   * @throws IOException if mkdirs is true but the directories cannot be created
   * @throws FileException if the external storage is not writeable
   * @throws PermissionException if the app doesn't have the necessary permissions to write the file
   */
  public static File getExternalFile(Form form, String fileName, FileScope scope,
      FileAccessMode accessMode, boolean mkdirs)
      throws IOException, FileException, PermissionException {
    File file = getExternalFile(form, fileName, scope);
    if (mkdirs) {
      // Create intermediate directories, if needed.
      if (accessMode != FileAccessMode.READ) {
        File directory = file.getParentFile();
        if (!directory.exists() && !directory.mkdirs()) {
          throw new IOException("Unable to create directory " + directory.getAbsolutePath());
        }
      }
    }
    return file;
  }

  /*
   *  Checks that external storage is mounted writeable. If it isn't,
   *  throws an exception.
   */
  public static void checkExternalStorageWriteable() throws FileException {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
      return;
    }
    if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
      throw new FileException(ErrorMessages.ERROR_MEDIA_EXTERNAL_STORAGE_READONLY);
    } else {
      throw new FileException(ErrorMessages.ERROR_MEDIA_EXTERNAL_STORAGE_NOT_AVAILABLE);
    }
  }

  /**
   * Given a file name of the form "name", "/name", or "//name", resolves the name to an absolute
   * file URI.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileName the name of the file to be accessed, using the File semantics
   * @param scope permission mode to use for locating the file and asking permissions
   * @return a String of the form "file://..." with the full path to the file based on the
   *     permission mode in effect
   */
  public static String resolveFileName(Form form, String fileName, FileScope scope) {
    File path;
    if (fileName.startsWith("//")) {  // Asset files in legacy mode
      path = new File(form.getAssetPath(fileName.substring(2)).substring(7));
    } else if (scope == FileScope.App && Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
      path = new File(form.getExternalFilesDir(""), fileName);
    } else if (scope == FileScope.Asset) {
      path = new File(form.getAssetPath(fileName).substring(7));
    } else if (scope == FileScope.Cache) {
      path = new File(form.getCachePath(fileName).substring(7));
    } else if ((scope == FileScope.Legacy || scope == null) && fileName.startsWith("/")) {
      path = new File(QUtil.getExternalStorageDir(form, false, true), fileName.substring(1));
    } else if (scope == FileScope.Private) {
      path = new File(form.getPrivatePath(fileName).substring(7));
    } else if (scope == FileScope.Shared) {
      path = new File(Environment.getExternalStorageDirectory() + "/" + fileName);
    } else if (!fileName.startsWith("/")) {  // Private files in legacy mode
      path = new File(form.getPrivatePath(fileName).substring(7));
    } else {
      /* Starting with nb186, files will be placed in different locations when the file name starts
       * with a single "/" character. For Android Q and later, this is the app-specific directory
       * on external storage. For Android versions prior to Q, it will be the root of the external
       * storage, such as /sdcard or /storage/external/0/.
       */
      path = getExternalFile(form, fileName.substring(1), scope);
    }
    return Uri.fromFile(path).toString();
  }

  /**
   * Given a scoped file, resolves the name to an absolute file URI.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param file the ScopedFile for which to obtain an absolute URI
   * @return a String of the form "file://..." with the full path to the file based on the
   *     permission mode in effect
   */
  public static String resolveFileName(Form form, ScopedFile file) {
    return resolveFileName(form, file.getFileName(), file.getScope());
  }

  /**
   * Checks whether the app will need permission to access {@code fileUri} due to it being in the
   * external storage directory. Starting with Android KitKat, READ/WRITE external storage
   * permissions are not required for files stored in the app-specific directory.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileUri the absolute URI to a file
   * @return true if the fileUri represents a file in external storage and permission will be
   *     needed to access it, otherwise false
   */
  public static boolean needsPermission(Form form, String fileUri) {
    if (isAssetUri(form, fileUri)) {
      return false;
    } else if (isPrivateUri(form, fileUri)) {
      return false;
    } else if (isAppSpecificExternalUri(form, fileUri)) {
      // App-specific directories don't need READ/WRITE permission on KitKat and higher.
      return Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT;
    } else {
      return isExternalStorageUri(form, fileUri);
    }
  }

  /**
   * Check whether the given scoped file needs the READ_EXTERNAL_STORAGE permission to be
   * granted to the app before the file can be read.
   *
   * @param scopedFile the scoped file to check for permission
   * @return true if the file will need READ_EXTERNAL_STORAGE permission, otherwise false
   */
  public static boolean needsReadPermission(ScopedFile scopedFile) {
    switch (scopedFile.getScope()) {
      case Legacy:
      case Shared:
        return true;
      default:
        return false;
    }
  }

  /**
   * Check whether the given scoped file needs the WRITE_EXTERNAL_STORAGE permission to be
   * granted to the app before the file can be written. Note: WRITE_EXTERNAL_STORAGE was removed
   * starting in Android 11 (SDK 30).
   *
   * @param scopedFile the scoped file to check for permission
   * @return true if the file will need WRITE_EXTERNAL_STORAGE permission, otherwise false
   */
  public static boolean needsWritePermission(ScopedFile scopedFile) {
    return needsWritePermission(scopedFile.getScope());
  }

  /**
   * Check whether the given file scope needs the WRITE_EXTERNAL_STORAGE permisison to be
   * granted to the app before files can be written to that scope. Note: WRITE_EXTERNAL_STORAGE was
   * removed starting in Android 11 (SDK 30).
   *
   * @param scope the file scope
   * @return true if the file scope needs WRITE_EXTERNAL_STORAGE permission, otherwise false
   */
  public static boolean needsWritePermission(FileScope scope) {
    switch (scope) {
      case Legacy:
      case Shared:
        // Write permission no longer exists in Android R
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.R;
      default:
        return false;
    }
  }

  /**
   * Check whether the given scoped file will cause access to external storage. This can be used to
   * determine whether a check for the mount state of the external storage is also needed. Note that
   * the file need not exist since we may want to create it.
   *
   * @param form the Form object to use as the Android context
   * @param scopedFile the scoped file to check against the external storage
   * @return true if operations on the scoped file will involve external storage, otherwise false
   */
  public static boolean needsExternalStorage(Form form, ScopedFile scopedFile) {
    return isExternalStorageUri(form,
        resolveFileName(form, scopedFile.getFileName(), scopedFile.getScope()));
  }

  /**
   * Checks whether the given {@code fileUri} represents an asset. In the case of a standard Form
   * object, this implies that the {@code fileUri} always starts with "file:///android_asset/".
   * For the REPL, the location will depend on the version of Android. For Android 10+, the REPL
   * stores its assets in the app-specific directory in external storage. Prior to Android 10, the
   * REPL stores its assets in AppInventor/assets in the root of the external storage.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileUri the absolute URI to a file
   * @return true if the fileUri represents an asset, otherwise false
   */
  public static boolean isAssetUri(Form form, String fileUri) {
    return fileUri.startsWith(form.getAssetPath(""));
  }

  /**
   * Checks whether the given {@code fileUri} represents a file in the app's private data
   * directory. For the REPL, "private" files are still stored on the external storage partition.
   * On Android 10 and later, the REPL places private files in a directory called data in the
   * app-specific directory. On Android versions prior to 10, the REPL places private files in
   * AppInventor/data in the root of the external storage.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileUri the absolute URI to a file
   * @return true if the fileUri represents a private data file, otherwise false
   */
  public static boolean isPrivateUri(Form form, String fileUri) {
    return fileUri.startsWith(form.getPrivatePath(""));
  }

  /**
   * Checks whether the given {@code fileUri} represents a file in the app-specific directory on
   * external storage. Because app-specific directories were introduced in SDK level 8
   * (Android 2.2 Froyo), this will always return false on earlier versions.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileUri the absolute URI to a file
   * @return true if the fileUri represents a file in app-specific external storage, otherwise false
   */
  public static boolean isAppSpecificExternalUri(Form form, String fileUri) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
      // App-specific directories were introduced in Android 2.2 Froyo; earlier versions don't have
      // an equivalent concept.
      return false;
    }
    return fileUri.startsWith("file://" + form.getExternalFilesDir("").getAbsolutePath());
  }

  /**
   * Checks whether the given {@code fileUri} represents a file in external storage.
   *
   * @param form the Form object to use as a Context and to ask for permissions, if needed
   * @param fileUri the absolute URI to a file
   * @return true if the fileUri represents a file in external storage, otherwise false
   */
  @SuppressWarnings({"unused", "deprecation"})
  public static boolean isExternalStorageUri(Form form, String fileUri) {
    if (fileUri.startsWith("file:///sdcard/") || fileUri.startsWith("file:///storage")) {
      return true;
    }
    return fileUri.startsWith("file://"
        + Environment.getExternalStorageDirectory().getAbsolutePath())
        || fileUri.startsWith("file://" + form.getExternalFilesDir("").getAbsolutePath());
  }

  /**
   * Exception class for reporting back media-related error numbers from
   * ErrorMessages, which the caller can in turn pass to
   * Form.dispatchErrorOccurredEvent if needed.
   */
  public static class FileException extends RuntimeError {
    private final int msgNumber;
    public FileException(int errorMsgNumber) {
      msgNumber = errorMsgNumber;
    }

    public int getErrorMessageNumber() {
      return msgNumber;
    }
  }

  public static String getNeededPermission(Form form, String path, FileAccessMode mode) {
    if (path == null) {
      throw new NullPointerException("path cannot be null");
    } else if (path.startsWith("file:") || path.startsWith("/")) {
      if (path.startsWith("/")) {
        path = "file://" + path;
      }
      if (isExternalStorageUri(form, path)) {
        if (isAppSpecificExternalUri(form, path)
            && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
          // On Android 4.4 (API level 19) or higher, your app doesn't need to request any
          // storage-related permissions to access app-specific directories within external storage.
          // https://developer.android.com/training/data-storage/app-specific?hl=en#external
          return null;
        } else if (mode == FileAccessMode.READ) {
          return READ_EXTERNAL_STORAGE;
        } else {
          return WRITE_EXTERNAL_STORAGE;
        }
      }
    } else if (!path.contains(":")) {
      throw new IllegalArgumentException("path cannot be relative");
    }
    return null;
  }

  /**
   * Move a file from one scope to another. The move will be attempted in an atomic way if
   * supported by the underlying system. IF not, the file will be copied and the original will be
   * deleted. It may be the case that the source file is read-only but isn't reported as such, in
   * which case the move operation will attempt to roll back by deleting the new copy.
   *
   * @param form the form to serve as a context for Android operations
   * @param src the source file
   * @param dest the destination file
   * @return true if the file was moved successfully, false otherwise
   * @throws IOException if an I/O exception occurs while copying or deleting the original file
   */
  public static boolean moveFile(Form form, ScopedFile src, ScopedFile dest) throws IOException {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
        && src.getScope() != FileScope.Shared && dest.getScope() != FileScope.Shared) {
      // Because Shared files go through the MediaStore, we cannot use the NIO API for moving.
      // New style. Use Java NIO to move the file, potentially between file system providers
      Path source = Paths.get(src.resolve(form).toURI());
      Path destination = Paths.get(dest.resolve(form).toURI());
      Files.move(source, destination);
      return true;
    } else {
      // Old style. Copy the file and then delete the original.
      byte[] buffer = new byte[4096];
      int read;
      InputStream in = null;
      OutputStream out = null;
      try {
        in = openForReading(form, src);
        out = openForWriting(form, dest);
        while ((read = in.read(buffer)) > 0) {
          out.write(buffer, 0, read);
        }
      } finally {
        IOUtils.closeQuietly(LOG_TAG, in);
        IOUtils.closeQuietly(LOG_TAG, out);
      }
      File original = src.resolve(form);
      File copy = dest.resolve(form);
      if (original.delete()) {
        // The file has been "moved"
        return true;
      } else if (copy.delete()) {
        // Deleted the copy since we couldn't remove the original
        return false;
      } else {
        // Made the copy but couldn't clean it up during rollback
        throw new IOException("Unable to delete fresh file");
      }
    }
  }

  /**
   * Remove a directory.
   *
   *
This method will recursively remove a directory if {@code recursive} is {@code true}.
   * However, this operation is not atomic, so it is possible that an exception can be thrown
   * and some files will have been deleted and others not.
   *
   * @param directory the directory to remove
   * @param recursive true if the directory's contents should be removed recursively, otherwise
   *                  false, in which case the directory must be empty
   * @return true if the directory was successfully removed
   * @throws IOException if an IO error occurs that prevents removal of the directory
   * @throws NullPointerException if {@code directory} is null
   * @throws IllegalArgumentException if {@code directory} does not represent a directory
   */
  public static boolean removeDirectory(File directory, boolean recursive) throws IOException {
    if (directory == null) {
      throw new NullPointerException();
    }
    if (!directory.isDirectory()) {
      throw new IllegalArgumentException();
    }

    File[] files = directory.listFiles();
    if (files == null) {
      return directory.delete();
    } else if (!recursive && files.length > 0) {
      return false;
    } else {
      boolean success = true;

      for (File child : files) {
        if (child.isDirectory()) {
          success &= removeDirectory(directory, recursive);
        } else {
          success &= child.delete();
        }
      }

      return success && directory.delete();
    }
  }

  /**
   * Open a {@link ScopedFile} for reading as an {@link InputStream}. The caller is responsible for
   * closing the stream returned by this method.
   *
   * @param form the form to use as an Android context
   * @param file the file to open for reading
   * @return a new input stream.
   * @throws IOException if the file cannot be opened
   */
  @SuppressWarnings("deprecation")
  public static InputStream openForReading(Form form, ScopedFile file) throws IOException {
    switch (file.getScope()) {
      case Asset:
        return form.openAsset(file.getFileName());
      case App:
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
          return new FileInputStream(new File(Environment.getExternalStorageDirectory(),
              file.getFileName()));
        }
        return new FileInputStream(new File(form.getExternalFilesDir(""), file.getFileName()));
      case Cache:
        return new FileInputStream(new File(URI.create(form.getCachePath(file.getFileName()))));
      case Legacy:
        return new FileInputStream(new File(Environment.getExternalStorageDirectory(),
            file.getFileName()));
      case Private:
        return new FileInputStream(new File(URI.create(form.getPrivatePath(file.getFileName()))));
      case Shared:
        String[] parts = file.getFileName().split("/", 2);
        Uri contentUri = getContentUriForPath(parts[0]);
        String[] projection = new String[] {
            MediaStore.Files.FileColumns._ID,
            MediaStore.Files.FileColumns.DISPLAY_NAME
        };
        Cursor cursor = null;
        try {
          cursor = form.getContentResolver().query(contentUri, projection,
              MediaStore.Files.FileColumns.DISPLAY_NAME + " = ?", new String[] { parts[1] }, null);
          int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
          if (cursor.moveToFirst()) {
            long id = cursor.getLong(idColumn);
            Uri targetUri = ContentUris.withAppendedId(contentUri, id);
            return form.getContentResolver().openInputStream(targetUri);
          }
        } finally {
          IOUtils.closeQuietly(LOG_TAG, cursor);
        }
        break;
      default:
        break;
    }
    throw new IOException("Unsupported file scope: " + file.getScope());
  }

  /**
   * Open a {@link ScopedFile} for writing as an {@link OutputStream}. The caller is responsible
   * for closing the stream returned by this method.
   *
   * @param form the form to use as an Android context
   * @param file the file to open for reading
   * @return a new input stream.
   * @throws IOException if the file cannot be opened
   */
  @SuppressWarnings("deprecation")
  public static OutputStream openForWriting(Form form, ScopedFile file) throws IOException {
    switch (file.getScope()) {
      case Asset:
        throw new IOException("Assets are read-only.");
      case App:
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
          return new FileOutputStream(new File(Environment.getExternalStorageDirectory(),
              file.getFileName()));
        }
        return new FileOutputStream(new File(form.getExternalFilesDir(""), file.getFileName()));
      case Cache:
        return new FileOutputStream(new File(URI.create(form.getCachePath(file.getFileName()))));
      case Legacy:
        return new FileOutputStream(new File(Environment.getExternalStorageDirectory(),
            file.getFileName()));
      case Private:
        return new FileOutputStream(new File(URI.create(form.getPrivatePath(file.getFileName()))));
      case Shared:
        String[] parts = file.getFileName().split("/", 2);
        final ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, parts[1]);
        values.put(MediaStore.MediaColumns.MIME_TYPE, "");
        values.put(MediaStore.MediaColumns.RELATIVE_PATH, parts[0]);
        final ContentResolver resolver = form.getContentResolver();
        Uri contentUri = getContentUriForPath(parts[0]);
        if (contentUri == null) {
          throw new IOException("Unrecognized shared folder: " + parts[0]);
        }
        Uri uri = resolver.insert(contentUri, values);

        if (uri == null) {
          throw new IOException("Unable to insert MediaStore entry for shared content");
        }

        OutputStream out = resolver.openOutputStream(uri);
        if (out == null) {
          throw new IOException("Unable to open stream for writing");
        }
        return out;
      default:
        break;
    }
    throw new IOException("Unsupported file scope: " + file.getScope());
  }

  /**
   * List the contents of a directory from a given scope.
   *
   * @param form the form to use as an Android context
   * @param file the directory to list
   * @return a list of files in the directory, or null if the given pathname was not a directory
   * @throws IOException if an I/O error occurs while reading the directory
   */
  @SuppressWarnings("checkstyle:FallThrough")
  public static List<String> listDirectory(Form form, ScopedFile file) throws IOException {
    switch (file.getScope()) {
      case Asset:
        if (!form.isRepl()) {
          // Assets are a special case since they are part of the APK, not on disk as individual files
          // ...except in the REPL, where they are stored in the app-specific directory.
          AssetManager manager = form.getAssets();
          String[] files = manager.list(file.getFileName());
          if (files != null) {
            return Arrays.asList(files);
          } else {
            return Collections.emptyList();
          }
        }
        // At this point, the processing logic is the same as all other types because of how the
        // REPL processes assets (which are typically stored in the App directory).
      case App:
      case Cache:
      case Legacy:
      case Private:
        File directory = new File(URI.create(resolveFileName(form, file.getFileName(), file.getScope())));
        String[] files = directory.list();
        if (files != null) {
          return Arrays.asList(files);
        }
        return null;
      case Shared:
        String filename = file.getFileName();
        if (filename.startsWith("/")) {
          filename = filename.substring(1);
        }
        String[] parts = filename.split("/", 2);
        final ContentResolver resolver = form.getContentResolver();
        Uri contentUri = getContentUriForPath(parts[0]);
        if (contentUri == null) {
          contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
        }
        Cursor cursor = null;
        try {
          String dataColumnName = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
              ? MediaStore.Files.FileColumns.DATA : MediaStore.Files.FileColumns.RELATIVE_PATH;
          final String[] columns = new String[] {
              MediaStore.Files.FileColumns.DISPLAY_NAME,
              dataColumnName
          };
          cursor = resolver.query(contentUri, columns, null, null, null);
          final int nameColumn = cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME);
          final int pathColumn = cursor.getColumnIndex(dataColumnName);
          final List<String> results = new ArrayList<>();
          final String rootPath = QUtil.getExternalStoragePath(form, false, true) + "/";
          while (cursor.moveToNext()) {
            String name = cursor.getString(nameColumn);
            String path = cursor.getString(pathColumn);
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
              results.add(path.replace(rootPath, ""));
            } else {
              results.add(path + name);
            }
          }
          return results;
        } finally {
          IOUtils.closeQuietly(LOG_TAG, cursor);
        }
      default:
        throw new IOException("Unsupported file scope: " + file.getScope());
    }
  }

  private static Uri getContentUriForPath(String path) {
    if ("DCIM".equals(path) || "Pictures".equals(path) || "Screenshots".equals(path)) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        return MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
      }
      return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    } else if ("Videos".equals(path) || "Movies".equals(path)) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        return MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
      }
      return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    } else if ("Audio".equals(path) || "Music".equals(path)) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        return MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
      }
      return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
        && ("Download".equals(path) || "Downloads".equals(path))) {
      return MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
      return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
    }
    return null;
  }
}


text:
function gestionnaireDeMenu(nb){
	if(nb == 1){
		createurDeMenu("modifications", "calendrier", "annuaire", "demandes", "ressources");
	}
	if(nb == 2){
		createurDeMenu("rappels", "calendrier", "annuaire", "demandes", "ressources");
	}
	if(nb == 3){
		createurDeMenu("rappels", "demandes", "calendrier", "annuaire", "ressources");
	}
	if(nb == 4){
		createurDeMenu("modifications", "calendrier", "demandes", "annuaire", "ressources");
	}
	if(nb == 5){
		createurDeMenu("rappels", "calendrier", "demandes", "annuaire", "ressources");
	}
	if(nb == 6){
		createurDeMenu("calendrier", "demandes","modifications", "annuaire", "ressources");
	}
	if(nb == 7){
		createurDeMenu("calendrier", "demandes", "rappels", "annuaire", "ressources");
	}
	if(nb == 8){
		createurDeMenu("modifications", "calendrier", "ressources", "demandes", "annuaire");
	}
	if(nb == 9){
		createurDeMenu("rappels", "calendrier", "ressources", "demandes", "annuaire");
	}
	if(nb == 10){
		createurDeMenu("modifications", "demandes", "calendrier", "annuaire", "ressources");
	}
}

function createurDeMenu(a, b, c, d, e){
	var nav = document.getElementById("navigation");

	var div1 = document.createElement('div');
	div1.className = "row";
	var div1_1 = document.createElement('div');
	div1_1.className = "col-lg-5 col-md-5 col-sm-5 col-xs-5";
	div1_1.id=a;
	var a1_1 = document.createElement('a');
	var img1_1 = document.createElement('img');
	img1_1.src = imgAssocié(a);
	a1_1.appendChild(img1_1);
	a1_1.href = lienAssocié(a);
	div1_1.appendChild(a1_1);
	div1.appendChild(div1_1);
	var div1_2 = document.createElement('div');
	div1_2.className = "col-lg-5 col-md-5 col-sm-5 col-xs-5";
	div1_2.id=b;
	var a1_2 = document.createElement('a');
	a1_2.href = lienAssocié(b);
	var img1_2 = document.createElement('img');
	img1_2.src = imgAssocié(b);
	a1_2.appendChild(img1_2);
	div1_2.appendChild(a1_2);
	div1.appendChild(div1_2);
	nav.appendChild(div1);

	var div2 = document.createElement('div');
	div2.className = "row";
	var div2_1 = document.createElement('div');
	div2_1.className = "col-lg-10 col-md-10 col-sm-10 col-xs-10";
	div2_1.id=c;
	var a2_1 = document.createElement('a');
	a2_1.href = lienAssocié(c);
	var img2_1 = document.createElement('img');
	img2_1.src = imgAssocié(c);
	a2_1.appendChild(img2_1);
	div2_1.appendChild(a2_1);
	div2.appendChild(div2_1);
	nav.appendChild(div2);

	var div3 = document.createElement('div');
	div3.className = "row";
	var div3_1 = document.createElement('div');
	div3_1.className = "col-lg-5 col-md-5 col-sm-5 col-xs-5";
	div3_1.id=d;
	var a3_1 = document.createElement('a');
	a3_1.href = lienAssocié(d);
	var img3_1 = document.createElement('img');
	img3_1.src = imgAssocié(d);
	a3_1.appendChild(img3_1);
	div3_1.appendChild(a3_1);
	div3.appendChild(div3_1);
	var div3_2 = document.createElement('div');
	div3_2.className = "col-lg-5 col-md-5 col-sm-5 col-xs-5";
	div3_2.id=e;
	var a3_2 = document.createElement('a');
	a3_2.href = lienAssocié(e);
	var img3_2 = document.createElement('img');
	img3_2.src = imgAssocié(e);
	a3_2.appendChild(img3_2);
	div3_2.appendChild(a3_2);
	div3.appendChild(div3_2);
	nav.appendChild(div3);
}

function imgAssocié(a){
	if (a=="modifications"){
		return("../img/Modifications.png");
	}
	if (a=="calendrier"){
		return("../img/CalendrierValidites.png");
	}
	if (a=="demandes" ){
		return("../img/Demandes.png");
	}
	if (a=="ressources"){
		return("../img/RessourcePeda.png");
	}
	if (a=="rappels"){
		return("../img/RappelsPerso.png");
	}
	if (a=="annuaire"){
		return("../img/Annuaire.png");
	}
}

function lienAssocié(a){
	if (a=="modifications"){
		return("modification");
	}
	if (a=="calendrier"){
		return("calendrier");
	}
	if (a=="demandes"){
		return("demandes");
	}
	if (a=="ressources"){
		return("ressources");
	}
	if (a=="rappels"){
		return("rappels");
	}
	if (a=="annuaire"){
		return("annuaire");
	}
}

// fonction pour faire disparaitre le menu de gauche lorsque la page est trop petite et faire reaparraitre celui ci si la fentre est assez grande.
function disparaitre(){
	if (window.matchMedia("(max-width: 650px)").matches){
		document.getElementById("navigation").parentNode.className="disparaitre";
		document.getElementById("navigation").className="disparaitre";
		document.getElementById("iconeMenu").className="iconeMenu glyphicon glyphicon-th";
		document.getElementById("section").className="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1";
		ouvrirPetitMenu();
	}
	else{
		document.getElementById("navigation").style.left=0;
		document.getElementById("navigation").className="col-lg-3 col-md-3 col-sm-3 col-xs-3";
		document.getElementById("section").className="col-lg-8 col-md-8 col-sm-8 col-xs-8";
		document.getElementById("iconeMenu").className="disparaitre";
		document.getElementById("navigation").parentNode.className="";
	}
}

// fonction pour ouvrir le menu lorsque l on clique sur l icone voulue.
function ouvrirPetitMenu (){
	document.getElementById("iconeMenu").onclick=function(){
		var width = window.innerWidth;
		if (width>500){
			var marginLeft = window.innerWidth*40/100;
		}
		else if (width>350){
			var marginLeft = window.innerWidth*30/100;
		}
		else if (width>200){
			var marginLeft = window.innerWidth*20/100;
		}
		document.getElementById("navigation").className="petitMenu";
		document.getElementById("navigation").style.left=marginLeft+"px";
		document.getElementById("navigation").parentNode.className="opaque";
	}
	document.getElementById("navigation").parentNode.onclick=function(){
		document.getElementById("navigation").parentNode.className="disparaitre";
	}
}

// fonction pour que le footer soit sois en bas de la page html si celle ci est plus grande que la fenetre sois en bas dela fenetre dans le cas inverse.
function gestionFooter(){
	var heightWindow = window.innerHeight;
	var heightSection = document.getElementById("section");
	var heightNavigation = document.getElementById("navigation");
	var heightSection2 = parseInt(getComputedStyle(heightSection).height, 10);
	var heightNavigation2 = parseInt(getComputedStyle(heightNavigation).height, 10);
  heightNavigation2 += 90;
	if (heightNavigation2>heightSection2){
		var res = heightWindow - heightNavigation2 - 70;
	}
	else{
		var res = heightWindow - heightSection2 - 70;
	}
	if (res>0){
		document.getElementById("footer").style.top=res+"px";
	}
	else{
		document.getElementById("footer").style.top=0;
	}
}

// lorsque le navigateur est redimensionné, vérifie la fonction disparaitre et la fonction gestion footer.
window.onresize=function(){
	disparaitre();
	gestionFooter();
}

// gestion des annonces de mailling.
function alerteMailling(){
	var i = 0;
	if (document.getElementsByClassName("close") != null){
		var tableauDesNotifs = document.getElementsByClassName("close");
		while (tableauDesNotifs[i] != undefined){
			i++;
		}
	}
	if (i>0){
		var p = document.getElementById("maillingAnnonceNum");
		p.innerHTML = i;
		p.className="apparaitre";
		var img =  document.getElementById("imgAlerte");
		img.className="apparaitre";
	}
	if(i==0){
		var p = document.getElementById("maillingAnnonceNum");
		p.className="disparaitre";
		var img =  document.getElementById("imgAlerte");
		img.className="disparaitre";
	}
}

function maillingAnnonce(){
	alerteMailling();
	document.getElementById("mailling").onclick=function(){
		alerteMailling();
	}
}

// gestion des notifications. (creations et suppression);
function supprimeurDeNotifications(){
		if (document.getElementById("notification") != null){
			document.getElementById("notification").onclick=function(){
				DELETE();
			}
		}
}

function DELETE(i){
	var element = document.getElementById("notification").parentNode;
	while (element.firstChild) {
  	element.removeChild(element.firstChild);
	}
	element.parentNode.removeChild(element);
	alerteMailling();
	supprimeurDeNotifications();
}

function createurDeNotifications(lvlAlerte, message){
	if (document.getElementById("notification") != null){
			DELETE();
	}
	var div = document.createElement('div');
	if (lvlAlerte == 1){
		div.className = "alert alert-success alert-dismissable fade in notification";
	}
	if (lvlAlerte == 2){
		div.className = "alert alert-info alert-dismissable fade in notification";
	}
	if (lvlAlerte == 3){
		div.className = "alert alert-warning  alert-dismissable fade in notification";
	}
	if (lvlAlerte == 4){
		div.className = "alert alert-danger alert-dismissable fade in notification";
	}
	var a = document.createElement('a');
	a.className = "close";
	a.datadismiss="alert";
	a.arialabel="close";
	a.innerHTML = "×";
	a.id = "notification";
	div.appendChild(a);

	var strong = document.createElement('strong');
	if (lvlAlerte == 1){
		strong.innerHTML = "Succès ! ";
	}
	if (lvlAlerte == 2){
		strong.innerHTML = "Info ! ";
	}
	if (lvlAlerte == 3){
		strong.innerHTML = "Attention ! ";
	}
	if (lvlAlerte == 4){
		strong.innerHTML = "Erreur ! ";
	}
	div.appendChild(strong);
	var span = document.createElement('span');
	span.innerHTML = message;
	div.appendChild(span);
	document.body.appendChild(div);

	supprimeurDeNotifications();
	alerteMailling();
}

redpajama_set_name: RedPajamaGithub


Heres what your IP address would look like to other users if you were using uTorrent without a VPN: And heres what your IP address would look like to other torrenters if you were protected by VPN: Check your IP now uTorrent proxy vs VPN.
wiFi. IP!,.,, -,what to free vpn top 5 do to create the virtual environment. Staying anonymous with tor. Virtual Private Networks (VPN)). Installing VirtualBox in a Windows 8.1 environment. Basic Linux terminal. How to install VirtualBox.
tV-. IPTV free vpn top 5 « » « »., asus,if you want to learn more about what the course contains, here is a short list of questions to help you decide free vpn top 5 if you should take it followed by a deep list of the course lectures below.as a result, the powers that be had to create a new addressing scheme to deal free vpn top 5 with this situation and developed IPv6.
thus fooling geoblocking mechanisms into thinking the user is located free vpn top 5 within the United States. Get a US IP Address: A American VPN this is how geoblocking / unblocking works: This IP address becomes the users public IP address,you can use the HMRC enquiry service. FAQ's free vpn top 5 (PDF)). If you can't find it, data kaip prisijungti vpn Protection We will only use the information you give us for electoral purposes. For further information on voter registration please read our.
Local. This causes the web browser to send the HTTP Request to NetScaler Gateway, which then forwards the HTTP Request to the internal web server. No VPN needed. Plug-in Type Windows/MAC Of both Clientless and ICA Proxy are set to Off, then the VPN Client.
by It's been a bad week for two free vpn top 5 of the world's biggest vendors of enterprise hardware and software Fortinet and Palo Alto Networks. Both companies fixed security issues this week affecting some of their most popular products,zero VPN VPN, vPN free vpn top 5 IP. Vip ip.upgrading to FortiOS FortiOS version officially supports upgrading from version, free vpn top 5 if you run FortiOS with FortiExtender, you must use a newer version of FortiExtender such as or later. To upgrade from other versions, see Supported Upgrade Paths. And.
5.5, iEEE 802.3, iPv6. IEEE 802.11b, iEEE 802.3u, iEEE 802.11n, 11Mbps 802.11g : 6,9,12,18,24,36,48,54Mbps 802.11n : up to 300Mbps. IEEE 802.11i, iEEE 802.11d, 2, product Segment N300 complete networking; 300Mbps Data Rate 802.11b : 1, iEEE 802.11e, iEEE torguard for windows 10 802.11g, iPv4,log into your ProtonVPN dashboard at m/login Select free vpn top 5 Downloads on in the left navigation bar. Download the ProtonVPN config files onto your phone. Find the OpenVPN configuration files section and chose.
Household electoral registration enquiry form Reigate and Banstead will be carrying out the statutory Annual Household Electoral Registration Canvass from Every household in the borough will receive a Household Enquiry Form detailing the names of all electors we have registered to vote at the property. You need to respond as soon.
After upgrading, if FortiLink mode is enabled, you must manually create an explicit firewall policy to allow RADIUS traffic for 802.1x authentication from the FortiSwitch (such as from the FortiLink interface) to the RADIUS server through the FortiGate. FortiGate-VM64-Azure upgrade You can upgrade from the.
class C is for smaller networks with the free vpn top 5 first 3 octets making up the netid and the last octet comprising the hostid. The Network ID and the Host ID are determined by a subnet mask.16 Remand prisoners, voluntary patients in mental hospitals and people free vpn top 5 without a fixed place of residence can register to vote by making a declaration of local connection.outside) source static LNGN ame LNGN ame destination static Azure- VNetName Azure- VNetName!! General IKEv2 configuration - enable IKEv2 for VPN! No NAT required between the on-premises network and Azure VNet! Nat (inside,) group-policy DfltGrpPolicy attributes vpn free vpn top 5 -tunnel-protocol ikev1 ikev2 exit! IKEv2 configuration!!
: IP. ZenMate Core VPN: -,.! 7 free vpn top 5 ZenMate Premium! ZenMate Premium?!pero este IP no es el suyo. Anonimato medio : host remoto sabe que usted esta utilizando free vpn top 5 un proxy, y cree que sabe su direccion IP,i've tried to imagine what it would be like to be a newcomer in current times and it's a bit depressing that most (not all)) of the material that is actually published is done free vpn top 5 so with an unhealthy bit of rodomontade and ulterior motives.there's more to setting up a virtual private network free vpn top 5 than just signing up and activating the service.
our TV boxes all have a 1-year warranty and over the air updates. View All Our Latest TV Boxes EBox MC is by far the best version free vpn top 5 of Kodi for Android. We have now released EBox MC for all Apple Devices.but ExpressVPN offers many other benefits that a free proxy service may not: ExpressVPN Proxy Hides your IP address Strong encryption Network Lock / kill free vpn top 5 switch 148 server locations No activity logs,the stock quot; example above also demonstrates the flexibility of enabling mass customization through independent components over that of a monolithic application. Monolithic Applications. More Flexible Independent Components vs. Not easy.
our tool is the most powerful proxy changer software on the market no other US proxy service, ready to try the USAs best IP service for FREE? Why wait? Sign free vpn top 5 Up for Proxy Rental Change IP Solution today. Click the Free Download Button.cisco VPN Client Windows 8 (32bit 64Bit)) - Reason 442: free vpn top 5 Failed To Enable Virtual Adaptor - How To Fix It article. Windows 8 32bit 64bit users dealing with the same problem can refer to our.l2TP Dual Link support Multicast Proxy support Utilities - Download master: Support BT, dHT, static IP, eD2K. L2TP Pass-Through WAN Connection Type Internet free vpn top 5 connection type : Automatic IP, pPPoE(MPPE supported PPTP,) nZB, support encryption, hTTP, pEX and magnet link.
.,.,by fax or by e-mail as a scanned attachment. As part of the Government's Digital By Default policy, 27 As of June free vpn top 5 2014, 26 Application forms can be returned to the local Electoral Registration Officer by post,in Babylon auctions of women for marriage were held annually. The auctions began with the free vpn top 5 woman the auctioneer considered to be the most beautiful and progressed to the least. 7 According to Herodotus,beeline WPA2-Personal.,. « WPA»,.
buy Wifi Display Dongle, free vpn top 5 wireless Screen Mirror,fR - free vpn top 5 Provence - Aix-en-Provence m. IP L2TP free DE - Bayern - Munich m. IP L2TP free NL - Noord-Holland - Amsterdam m. IP L2TP free. CA - Ontario - Toronto m.


text:
Nyetimber Mill  is a grade II listed tower mill at Pagham, Sussex, England which has been converted to residential use.

History

Nyetimber Mill was built in the early 1840s and was working until tailwinded in 1915. The sails were on the mill until they were struck by lightning in 1927. The mill became derelict and was burnt out in 1962, leaving the windshaft perched above the ivy covered tower.  The mill was converted to a house by 2005.

Description

Nyetimber Mill is a four-storey brick tower mill with an ogee cap. It had four Patent sails and was winded by a fantail. The mill drove two pairs of millstones. An external pulley enabled the mill to be worked by an engine. This has been retained in the converted mill.

Millers

William Adams 1840s - 1905
W Prior - 1915

References for above:-

References

External links
Windmill World Page on Nytimber  windmill.

Further reading
 Online version

Industrial buildings completed in the 19th century
Towers completed in the 1840s
Tower mills in the United Kingdom
Grinding mills in the United Kingdom
Grade II listed buildings in West Sussex
Windmills in West Sussex
1840s establishments in England

redpajama_set_name: RedPajamaWikipedia


Designed by the highly regarded British architect David Wordsworth, Ngapali Bay Villas & Spa is a luxury boutique hotel with an excellent location on Ngapali Beach, near local restaurants and the charming fisherman's village. This boutique resort has 32 detached villas each with a view of the ocean through the floor to ceiling windows. The villas are decorated with elegance and style reflected in the use of fine cotton, white marble, and teak wood furnishings. Facilities at the hotel include the Frangipani Spa which offers the best of Eastern and Western massage techniques, a seafront bar / restaurant and an outdoor swimming pool.
The Ngapali Bay brings a touch of sophistication to the beach scene in Myanmar. Elegant rooms go hand in hand with a great beach front location.


When most people think about America's debt problem, they think of the debt of the federal government. But that is only part of the story. The sad truth is that debt slavery has become a way of life for tens of millions of American families. Over the past several decades, most Americans have willingly allowed themselves to become enslaved to debt. These days, most of us are busy either going into even more debt or paying off the debt that we have accumulated in the past.
When your finances are dominated by debt, it makes it really hard to ever get ahead. Incredibly, 43 percent of all American families spend more than they earn each year. Even while median household income continues to decline (now less than $50,000 a year), median household debt continues to go up. According to the Federal Reserve, median household debt in America has risen to $75,600. Many Americans spend decades caught in the trap of debt slavery. Large numbers of them never even escape at all and die in debt. It can be a lot of fun to spend lots of money and go into lots of debt, but it can be absolutely soul crushing to toil and labor for years paying off those debts while making others wealthy in the process. Hopefully this article will inspire many people to try to escape the chains of debt slavery once and for all.
Because the truth is that the American people need a wake up call. Consumer borrowing rose by another $19.3 billion in December. Right now it is sitting at a grand total of $2.5 trillion according to the Federal Reserve.
Overall, consumer debt in America has increased by a whopping 1700% since 1971.
We always criticize the federal government for going into so much debt, but we rarely criticize ourselves for our own addiction to debt.
Debt slavery is destroying millions of lives all across this country, and it is imperative that we educate the American people about the dangers of all this debt.
The following are 30 facts about debt in America that will absolutely blow your mind….
#1 Today, 46% of all Americans carry a credit card balance from month to month.
#2 Overall, Americans are carrying a grand total of $798 billion in credit card debt.
#3 If you were alive when Jesus was born and you spent a million dollars every single day since then, you still would not have spent $798 billion by now.
#4 Right now, there are more than 600 million active credit cards in the United States.
#5 For households that have credit card debt, the average amount of credit card debt is an astounding $15,799.
#7 The average interest rate on a credit card that is carrying a balance is now up to 13.10 percent.
#8 According to the credit card calculator on the Federal Reserve website, if you have a $10,000 credit card balance and you are being charged a rate of 13.10 percent and you only make the minimum payment each time, it will take you 27 years to pay it off and you will end up paying back a total of $21,271.
#9 There is one credit card company out there, First Premier, that charges interest rates of up to 49.9 percent. Amazingly, First Premier has 2.6 million customers.
#10 The length of auto loans in America just keeps getting longer and longer. If you can believe it, 45 percent of all new car loans being made today are for more than 6 years.
#11 Approximately 70 percent of all car purchases in the United States involve an auto loan.
#12 A subprime auto loan bubble is steadily building. Today, 45 percent of all auto loans are made to subprime borrowers. At some point that is going to be a massive problem.
#13 Total home mortgage debt in the United States is now about 5 times larger than it was just 20 years ago.
#14 Mortgage debt as a percentage of GDP has more than tripled since 1955.
#15 According to the Mortgage Bankers Association, approximately 8 million Americans are at least one month behind on their mortgage payments.
#16 Historically, the percentage of residential mortgages in foreclosure in the United States has tended to hover between 1 and 1.5 percent. Today, it is up around 4.5 percent.
#17 According to Dylan Ratigan, 46 percent of all mortgaged properties in Florida are underwater, 50 percent of all mortgaged properties in Arizona are underwater and 63 percent of all mortgaged properties in Nevada are underwater.
#18 Overall, nearly 29 percent of all homes with a mortgage in the United States are underwater.
#19 If you can believe it, the mortgage lenders now have more equity in U.S. homes than the American people do.
#20 Medical debt is a major problem for a growing number of Americans. One study discovered that approximately 41 percent of all working age Americans either have medical bill problems or are currently paying off medical debt.
#21 Sadly, the number of Americans that are protected by health insurance continues to decline. An all-time record 49.9 million Americans do not have any health insurance at all right now, and the percentage of Americans covered by employer-based health plans has fallen for 11 years in a row.
#22 But even if you do have health insurance, there is still a good chance that you could end up with huge medical debt problems. According to a report published in The American Journal of Medicine, medical bills are a major factor in more than 60 percent of the personal bankruptcies in the United States. Of those bankruptcies that were caused by medical bills, approximately 75 percent of them involved individuals that actually did have health insurance.
#23 Total student loan debt in the United States is rapidly approaching 1 trillion dollars.
#24 If you went out right now and starting spending one dollar every single second, it would take you more than 31,000 years to spend one trillion dollars.
#25 In America today, approximately two-thirds of all college students graduate with student loan debt.
#26 The average student loan debt load is now approximately $25,000.
#27 After adjusting for inflation, U.S. college students are borrowing about twice as much money as they did a decade ago.
#28 One survey found that 23 percent of all college students actually use credit cards to pay for tuition or fees.
#29 The student loan default rate has nearly doubled since 2005.
#30 Student loans made to directly to parents have increased by 75 percentsince the 2005-2006 academic year.
At this point, most Americans are up to their eyeballs in debt. According to a recent study conducted by the BlackRock Investment Institute, the ratio of household debt to personal income in the United States is now 154 percent.
Our entire economy has become based on credit.
Just get an auto loan.
Do you need to fill up your house with stuff?
Just get a credit card.
Just get a student loan.
In fact, if you are anything like a typical American, you probably have a mortgage you can barely afford, you probably have at least one auto loan, you probably have several credit card balances and you probably have a student loan that you deeply regret.
So what should you do if you are drowning in debt?
First, make a firm decision that you are going to break the chains of debt slavery once and for all.
Secondly, come up with a plan to reduce your debt. Paying off debt that carries a high rate of interest first (such as credit card debt) is usually a good idea.
The big financial institutions want to get us into as much debt as possible, because all of this debt makes them incredibly wealthy.
Yes, that may mean that you may have to put off certain purchases until you can come up with the money, but in the long run you will be much better off.
So do any of you have any debt slavery stories to share? Please feel free to leave a comment with your thoughts below….


Oil prices recovered to near $101 a barrel on Tuesday after falling on news that a US credit rating agency may cut the debt ratings of 15 euro zone countries.
By afternoon in Europe, benchmark crude for January delivery was up 4 cents to $101.03 a barrel in electronic trading on the New York Mercantile Exchange. In London, Brent crude was up 52 cents at $110.33 on the ICE Futures exchange.


text:
The complete guide to: Siem Reap travel
Travelling to Cambodia and checking out the sights in Siem Reap? This guide will give you everything you need to know about visiting Siem Reap, from getting to and from the airport, to the best Siem Reap-themed books you should read before you go, to the cool local spots you should check out.
(Only visiting for a short time? Check out our savvy 24 hours in Siem Reap guide for one perfect day in the city.)
Getting to and from REP
Taxis are the best way to get to and from the airport — they are safe to use and not metered. The official booth outside the airport's domestic and international exit has a set price and ticket system. Note that taxi drivers are often also touting for additional businesses such as day tours to the temples.
Alternately, you can hire a remork (moto-rickshaw or Cambodian tuk-tuk). Official airport remorks are organized at the taxi stand next to the airport terminal. Note that, as with taxi drivers, remork drivers are often also touting for tours.
Taxi one-way: approx. USD 7 to city centre
Remork one-way: approx. USD 5 to city centre
Some hotels and guesthouses offer free airport pick-up/drop-off by taxi or remork. Check if your accommodation offers this before you arrive.
The journey from the airport to downtown Siem Reap takes 20–30 minutes, depending on traffic.
Getting around Siem Reap
Public transit: Sorry, but you're out of luck here, as there is no public transport system in Siem Reap. Most locals use motorbike taxis (motos) or remorks for short journeys around town if they don't have their own vehicle. However, there is public transport between major towns and cities such as from Siem Reap to Phnom Penh.
Taxi: You can safely hire a taxi from any of the reputable hotels and guesthouses in Siem Reap. But that being said, traditional taxis aren't common in Siem Reap. Instead, there are cars and minibuses available for hire with a driver, from stands near the Old Market and Siem Reap River. However, these drivers are generally seeking longer journeys between towns and cities in Cambodia. Note that they are not marked as taxis and are not metered. Rates start at approximately USD 35 per day (subject to other surcharges for distance, early departure, etc.).
Remork: Most visitors and many locals travel by remork. Short journeys around town will cost USD 2–3 per remork (seats up to four people) and the remork can be hired for USD 15–20 per day, depending on the duration and distance. Expect to pay more to go to outlying temples and for sunrise trips. Recently, some hybrid "tuxis" (half tuk-tuk, half taxi) have been making the rounds in Siem Reap. They charge USD 1 per person for short journeys such as from the Old Market to a central hotel.
Walking: Siem Reap is a great city to explore on foot. It is generally safe although there are the odd incidents of bag snatching. Take the usual precautions such as leaving your passport and valuables in the safe at the hotel, and walk with a group of friends, especially late at night. The walk along the river from the Royal Park in front of Raffles Hotel to Old Market is charming. Walking is also a great way to explore the narrow passageways, lanes, and alleyways around Pub Street and the Old Market.
Bicycle: Biking is a great way to explore the city and temples of Angkor. Local bikes cost USD 1–2 per day to hire, while recent model mountain bikes cost USD 8–10 per day. Hire your bike from the White Bicycles NGO — their projects help the local community and the bikes are available for hire from a number of hotels and guesthouses around town. Or, if you're after recent model mountain bikes, there are a number of places renting bikes near the Old Market and Wat Bo Road.
For a true taste of Siem Reap as a local, how about some morning aerobics? Trust us, there's usually not a foreigner in sight, and you'll burn off at least some of the snacks you ate at the night market the evening before. The scene is a combination of popular dance and aerobics, led by an instructor with a boombox. Play recognize-that-song with the tunes, which are often Western favourites paired with Khmer lyrics. There are a number of places around town that offer the sessions, including the grounds of the Cambodian People's Party community centre (located along the river, opposite the park with the concrete animals). The sessions run both morning and evening, usually about 6am–7am and 5pm–6:30pm (according to sunrise and sunset). The cost is about 2,000 riel, or USD 0.50 per person.
Or, make some new friends by playing sai — the Cambodian version of hacky sack. Buy the "ball" from a toy, stationery, or general store for about US 1, then pull it out and go. You'll usually spot kids and adults alike playing in the late afternoon.
The Phare Cambodian Circus is a unique blend of Cambodian acrobatics, contortion, juggling, fire stick throwing, traditional music, dance, and tightrope walking incorporated into stories that tackle contemporary themes. The show isn't just entertaining — it's also for a good cause. Phare performers are all from vulnerable households and the circus gives them a platform on which to build their artistic careers. Profits from the show support the educational and social programs of Phare.
The daily show starts at 7:30pm (get there early, as there's no entry permitted after 7:35pm). Tickets cost USD 15 for adult "class B" seats. For reserved "class A" front-row seats, the price is USD 35 for adults. Shows change every few weeks.
For another cultural show for a good cause, you can check out Beatocello at Jayavarman VII Children's Hospital, located on Charles d'Gaulle Road. The founder of the hospital, Dr. Beat Richner, puts on regular cello performances, which are accompanied by a presentation on the activities of the hospitals, and attendees can make donations of either blood or money.
Lastly, check out one of the traditional Apsara dance performances. These colourful shows recreate the 1,000-year-old scenes of court dancing carved on the walls of the temples. Performances vary from free with the purchase of a meal from an à la carte menu, to USD 40 or more at 5-star hotels, which usually includes a three-course set menu. For cheap and cheerful, Temple Balcony on Pub Street has daily performances from 7:30–9:30pm that are free when you dine in their restaurant.
Alternately, we love the Apsara Theatre (in front of Angkor Village Hotel off Wat Bo Road) for the beauty of the performance hall, the stunning costumes, and the skill of the dancers. The cost is USD 25 and includes a (admittedly bland) three-course set Khmer menu. Bookings are recommended and seating is generally Asian-style at low tables with cushions on the floor. You can also request in advance to have balcony seats at tables. Shows normally start at 7:30pm.
If you plan to be in Siem Reap in April, then you should definitely check out Angkor Songkran, or Khmer New Year, during which you can participate in traditional Khmer games, dancing, and martial arts, watch shows, listen to Cambodian music, and enjoy local snacks and picnics. It's the country's number-one public holiday and a chance to see locals celebrating in true Cambodian style.
The annual event runs April 14 to 16 and a schedule of events and locations is published in the Phnom Penh Post newspaper in the days leading up to it. Most advertised events are in sites around Angkor Wat and Angkor Thom temple, along the riverfront, and around Old Market and Pub Street. There are also local events at many pagodas outside the city.
Looking for more things to do? Urban Adventures offers several day tours in Siem Reap, all led by local experts that will give you a taste for local life in the city:
Tonle Sap Secrets
When the heat and crowds of the city get a bit much, join this Siem Reap tour to spend a day out on the water among the floating villages and farms of Tonle Sap. Recharge on the lake where villagers fish and farm is the perfect antidote to Angkor overload (it happens).
Angkor Adventure
Like a good blockbuster, this best-selling Siem Reap tour is entertaining, enthralling, and hard to pass up. See Angkor's most famed temples from the serene smiling faces of Bayon to the tomb-raiding Ta Prohm, before devouring a Khmer feast fit for a king.
Siem Reap Street Food by Night
Siem Reap's street food has also been influenced by Thai, Vietnamese, Chinese, and French techniques to form a style and flavour profile that is uniquely Cambodian. On this night-time food tour, we'll hop on local transport and travel away from the tourist trail to sample authentic Siem Reap snacks, desserts, and Khmer cuisine staples.
Cambodian Cook-out
Get to the heart of Cambodia — through its food! Take a trip into the Cambodian countryside and show off your cooking skills with this hands-on culinary adventure. You'll whip up a 3-course gourmet meal, get to experience local village life, and, best of all, eat your culinary creations.
Siem Reap on the big screen
Before you visit, get excited for the sites by watching Lara Croft: Tomb Raider or Two Brothers, which were both shot at some of the famously spectacular temples of Angkor. Another recommendation for those wishing to better understanding Cambodia's past is The Killing Fields — it explains the tragic era of the Khmer Rouge.
Siem Reap on record
The Cambodian Space Project will give you a surreal taste of Cambodian pop culture and history all wrapped up in one. The band, featuring Cambodian singer Srey Channthy, tours internationally but remains deeply rooted in Cambodia, with particular dedication to the country's rural, poverty-stricken regions. Their mix of psychedelic rock and political and cultural messaging is uniquely Cambodian — their music is heavily influenced by 1960s Cambodian pop that was lost in the Khmer Rouge regime. Their most recent album is Whisky Cambodia.
For music that paints a vivid picture of the Vietnam/American War and Khmer Rouge era, listen to punk song Holiday in Cambodia by the Dead Kennedys and Cambodia by Kim Wilde, both from the early '80s.
Khemarak Sereymon is a popular Cambodian singer; listen to Khmer New Year Song for a taste.
Cambodia in books
While most books about Cambodia focus on the Khmer Rouge, A Dragon Apparent, originally published in 1951, provides a picture of Cambodia, Vietnam, and Laos prior to the Vietnam/America war and Pol Pot rule.
For stories of the hardship Cambodia went through in those later years, First they Killed My Father provides a well-written and sometimes heart-wrenching survivor's account of the lead-up to the Khmer Rouge seizing power in Cambodia, the labour camps, and finally the journey to freedom. Another recommended read for that era is The Gate, which is the true story of Francois Bizot, a French scholar who arrived in Cambodia in 1965. The book explores his experience of being imprisoned by the Khmer Rouge and escaping Cambodia after the fall of Phnom Penh in 1975.
What's on in Siem Reap
For info on everything that's happening in Siem Reap, the Friday edition of the Phnom Penh Post includes the Siem Reap Insider with details of city news and events for the coming week. As well, you can check out Wat's Up!, a closed Facebook group that you can ask to join and that has information about events in Siem Reap.
Give us a ring at +855 63 966 655 (Monday to Friday from 8am–12pm and 2pm–6pm). Outside office hours and on public holidays and weekends, you can reach us at +855 12 391 780. Our emergency number is +855 92 555 969.
You can also email us at bookings@siemreapurbanadventures.com or follow us on Instagram.
cambodia travel tips2 complete guides11 siem reap travel guide2 travel to siem reap2 what to do in siem reap2
How to survive Toronto Nuit Blanche
24 Hours In
24 hours in: Phnom Penh
Urban Adventures. Quite simply, the Best. Day. Ever.
James Limo#1
I really liked your article as I am traveling lover 🙂 Keep on sharing such kind of articles
Discover the world's best street food
Subscribe to our e-mail Newsletter for the latest news and special offers.
Book a Tour Today

redpajama_set_name: RedPajamaCommonCrawl


I have the most recent build (3.5) of the dolphin emulator, which I downloaded primarily to play The Legend of Zelda Wind Waker. I reached the part of the game where Aryll gives you her telescope, and I assigned it to a button as directed. To my dismay, upon activating the telescope, the screen goes completely black save for the telescope controls and Aryll's dialogue. All of the buttons to my controller were useless, although they made the corresponding "no use" noises in-game. The game is unplayable from there and has to be shut down. Is this an emulator issue, or a ROM issue? I'm using the .iso version downloadable from ***********.
NOTE: this isn't a repeated thread, I've checked. The other issues are with the telescope cutscene hanging.
(01-26-2013, 11:21 AM)DurzasBane99 Wrote: I have the most recent build (3.5) of the dolphin emulator, which I downloaded primarily to play The Legend of Zelda Wind Waker. I reached the part of the game where Aryll gives you her telescope, and I assigned it to a button as directed. To my dismay, upon activating the telescope, the screen goes completely black save for the telescope controls and Aryll's dialogue. All of the buttons to my controller were useless, although they made the corresponding "no use" noises in-game. The game is unplayable from there and has to be shut down. Is this an emulator issue, or a ROM issue? I'm using the .iso version downloadable from ***********.
I have not had a problem with that happening in WW, but the problem I was having was that after certain cutscenes, my screen would stay black. If you right click the game with dolphin open, go to properties, then go to the patches tab, there is a patch called fix item hang, try that and see if it works, it worked for me. Only thing though is that when you use that cheat, you will have no audio for the game until you turn it off. Last thing, it is best not to mention where you get your ISOs from, you do not want to get in trouble.


We all know by now that a vegan diet has several health benefits, including lowering blood pressure and cholesterol, which in turn reduces the risk of heart disease, cancer and type 2 diabetes. Eating more veg and less dairy can also make your skin glow, and don't worry about the protein factor: plant based foods are actually rich in the stuff, and while vegans may have to take B12 and iron supplements, the benefits of eating more whole grains, vegetables and legumes that are low in fat and high in fibre are immense.
So no wonder that a wide range of celebrities including Usher, Russell Brand, Ellen Degeneres and Olivia Wilde–all of whom need to look and feel their best at all times–are turning to vegan diets, and now that includes superdiva, Beyonce.
The star is so impressed with the results of her vegan diet that she's not only changing her own way of eating, but she's trying to encourage others to change, too. She's just teamed up with her personal trainer, Marco Borges, to create an on-demand vegan meal service called 22 days nutrition. Why 22 Days? Because apparently it takes a full 21 days for your body to cleanse itself of its old, toxic habits and get used to a better way of life.
Every meal in Bey and Marco's plan is 100 per cent plant-based and delivered once a week, with all ingredients being non-GMO, gluten-free, soy-free, dairy-free and organic. Prices are reasonable and start as low as $9.24 ( £6.15) per meal– you can choose to have one, two or three meals delivered per day.
Marco says: 'We all know the importance and value of eating plant-based foods but often times find ourselves trapped in a series of bad habits that sabotage optimum wellness. The Vegan Meal Delivery program makes it easier to reset your habits with healthy and delicious plant-based foods.' Sample dishes, which were shown on Beyonce's Instagram account, include fried rice with curried veg, almond berry breakfast loaf, and Mexican fajita brekkie beans.
This isn't the singer's first go at being a vegan: she, along with her husband Jay Z, changed their diets at the end of 2013, and found that they had both dramatically slimmed down after following a healthy vegan eating plan.
Wow! I am so pleased that Beyonce has decided to go vegan again. she must realise that it is an healthy diet. Hence working with her personal trainer Marcos Borges on creating vegan meals and the Vegan Meal Delivery program to assist her many fans to follow Beyonce's excellent example. She must realise the ethical value of this as well. It means less animals will need to be slaughtered. She is obviously a person of great insight to promote such a caring humane way of eating.
I love that her carnivorous core has changed and flexed so much she now has a vegan core. What is most admirable is that she has been able to present a wonderful duality to the world. Beyonce has now a vegan core. While her fashion core still allows her to wear head to toe leather with utter ease and self confidence. I admire her ability to do this she looks fabulous in leather while promoting an inner peace for her health. A beautiful mix of Vegan Yin and Leather Yan.


text:
Disappointed Felix had nothing more to give
Shaunae Miller's dive over the finish line had given the Bahamian victory in the 400 meters at Rio 2016 by seven one-hundredeths of a second. Allyson Felix, who came second, couldn't believe it.
Marc Lancaster
16 August, 2016 15:32 IST
Shaunae Miller pipped Allyson Felix in a nerve-wracking 400m finish.
All Allyson Felix knew in the moments after she crossed the finish line on Monday was that it had been close. And that Shaunae Miller was already prone on the track next to her.
But the reality set in with one glance at the screen displaying the results – Miller's dive over the finish line had given the Bahamian victory in the 400 meters at Rio 2016 by seven one-hundredeths of a second.
It was a realisation that left the American deflated.
"I wasn't quite sure who'd won, you never are when it's that close," Felix said. "It's always hard after a 400 and I was physically and emotionally drained. I was just thinking 'disappointment.' It has been a tough year, but I just really wanted it."
The four-time gold medallist looked to be reeling Miller in after her opponent's fast start, but could not beat her to the line.
"I tried to give it all I have, but I didn't have any more to give in the last 10 meters," she said. "I'll have to look back at the race but I might have been a bit more aggressive."
Near tears as she met with the media later, Felix said she never thought about lunging like Miller did. Miller herself conceded it was something she had never done before.
But missing out on gold by that margin, with memories of her back-to-back silvers in the 200m behind Veronica Campbell-Brown in 2004 and 2008 inevitably there to recall, had Felix lamenting a missed opportunity in Rio.
"It's a little bittersweet now," she added. "I'm a competitor and I went for it, so in this moment it's painful."
Live India vs Australia 4th Test Live Cricket Score Online Day 4 Updates: Shardul gets Green to leave Australia six down; lead crosses 250