update docs in the source
This commit is contained in:
parent
8441bb99d2
commit
4fe0e8d2a4
4
build.rs
4
build.rs
|
@ -15,10 +15,6 @@ fn main() {
|
|||
.build("data.rs")
|
||||
.unwrap();
|
||||
|
||||
// TODO this using cargo as a Makefile. This is only for development, it
|
||||
// doesn't have to be part of the production auto-build. Use either a
|
||||
// Makefile or an npm command if stylus comes from npm anyway.
|
||||
|
||||
if let Ok(_) = env::var("CARGO_FEATURE_REGENERATE_CSS") {
|
||||
|
||||
// Compile stylus stylesheet to css
|
||||
|
|
|
@ -35,7 +35,7 @@ impl Book {
|
|||
book
|
||||
}
|
||||
|
||||
/// Parses in the SUMMARY.md or creates one
|
||||
/// Parses the SUMMARY.md or creates one
|
||||
pub fn parse_or_create_summary_file(&mut self, first_as_index: bool) -> Result<&mut Self, String> {
|
||||
|
||||
let summary_path = self.config.src.join("SUMMARY.md");
|
||||
|
@ -81,7 +81,7 @@ impl Book {
|
|||
content
|
||||
}
|
||||
|
||||
// TODO update
|
||||
// These don't seem to be used.
|
||||
|
||||
// /// This method takes a slice `&[x, y, z]` as parameter and returns the corresponding chapter.
|
||||
// /// For example, to retrieve chapter 2.3 we would use:
|
||||
|
|
|
@ -16,8 +16,6 @@ pub struct BookConfig {
|
|||
|
||||
// Paths
|
||||
|
||||
// TODO test if dest and src behaves correctly when mdbook.dest_base and mdbook.src_base is not 'book' and 'src'
|
||||
|
||||
pub dest: PathBuf,
|
||||
pub src: PathBuf,
|
||||
|
||||
|
@ -25,8 +23,9 @@ pub struct BookConfig {
|
|||
|
||||
/// The title of the book.
|
||||
pub title: String,
|
||||
/// The subtitle, when titles are in the form of "The Immense Journey: An
|
||||
/// Imaginative Naturalist Explores the Mysteries of Man and Nature"
|
||||
/// The subtitle, when the book's full title is in the form of "The Immense
|
||||
/// Journey: An Imaginative Naturalist Explores the Mysteries of Man and
|
||||
/// Nature"
|
||||
pub subtitle: Option<String>,
|
||||
/// A brief description or summary of the book.
|
||||
pub description: Option<String>,
|
||||
|
@ -94,7 +93,7 @@ impl BookConfig {
|
|||
/// write data back to it.
|
||||
///
|
||||
/// Parses author when given as an array, or when given as a hash key to
|
||||
/// make declaring just an author name easy.
|
||||
/// make declaring a single author easy.
|
||||
///
|
||||
/// Both of these express a single author:
|
||||
///
|
||||
|
|
|
@ -21,7 +21,7 @@ use utils::fs::create_with_str;
|
|||
/// If the markdown file starts with a TOML header, it will be parsed to set the
|
||||
/// chapter's properties. A TOML header should start and end with `+++` lines:
|
||||
///
|
||||
/// ```
|
||||
/// ```text
|
||||
/// +++
|
||||
/// title = "The Library of Babel"
|
||||
/// author = "Jorge Luis Borges"
|
||||
|
@ -81,8 +81,6 @@ pub struct Chapter {
|
|||
/// The description of the chapter.
|
||||
pub description: Option<String>,
|
||||
|
||||
/// TODO use this in the template
|
||||
|
||||
/// CSS class that will be added to the page-level wrap div to allow
|
||||
/// customized chapter styles.
|
||||
pub css_class: Option<String>,
|
||||
|
@ -235,43 +233,6 @@ impl Chapter {
|
|||
self
|
||||
}
|
||||
|
||||
// TODO not being used?
|
||||
|
||||
// /// Reads in the chapter's content from the markdown file. Chapter doesn't
|
||||
// /// know the book's src folder, hence the `book_src_dir` argument.
|
||||
// pub fn read_content_using(&self, book_src_dir: &PathBuf) -> Result<String, Box<Error>> {
|
||||
// let p = match self.get_src_path() {
|
||||
// Some(x) => x,
|
||||
// None => {
|
||||
// return Err(Box::new(io::Error::new(
|
||||
// io::ErrorKind::Other,
|
||||
// format!("src_path is None"))
|
||||
// ));
|
||||
// }
|
||||
// };
|
||||
|
||||
// let src_path = book_src_dir.join(&p);
|
||||
|
||||
// if !src_path.exists() {
|
||||
// return Err(Box::new(io::Error::new(
|
||||
// io::ErrorKind::Other,
|
||||
// format!("Doesn't exist: {:?}", src_path))
|
||||
// ));
|
||||
// }
|
||||
|
||||
// debug!("[*]: Opening file: {:?}", src_path);
|
||||
|
||||
// let mut f = try!(File::open(&src_path));
|
||||
// let mut content: String = String::new();
|
||||
|
||||
// debug!("[*]: Reading file");
|
||||
// try!(f.read_to_string(&mut content));
|
||||
|
||||
// content = utils::strip_toml_header(&content);
|
||||
|
||||
// Ok(content)
|
||||
// }
|
||||
|
||||
pub fn get_src_path(&self) -> Option<PathBuf> {
|
||||
self.src_path.clone()
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ pub struct MDBook {
|
|||
/// folder with the chapter files.
|
||||
///
|
||||
/// In the case of a single language, it is the sole item in the HashMap,
|
||||
/// and its source is not expected to be under a sub-folder, just simply in
|
||||
/// `./src`.
|
||||
/// and its Markdown files are not expected to be under a sub-folder, just
|
||||
/// simply in `./src`.
|
||||
///
|
||||
/// Translations have to be declared in `book.toml` in their separate
|
||||
/// blocks. The first in the TOML config will be recognized as the main
|
||||
|
@ -82,25 +82,24 @@ pub struct MDBook {
|
|||
/// author = "Lewis Carroll"
|
||||
/// ```
|
||||
///
|
||||
/// For multiple languages, declare them in blocks:
|
||||
/// For multiple languages, declare them in blocks. The translation key will
|
||||
/// be the language code. Optionally, the language name can be set as well.
|
||||
///
|
||||
/// ```toml
|
||||
/// [[translations.en]]
|
||||
/// title = "Alice in Wonderland"
|
||||
/// author = "Lewis Carroll"
|
||||
/// language = { name = "English", code = "en" }
|
||||
///
|
||||
/// [[translations.fr]]
|
||||
/// title = "Alice au pays des merveilles"
|
||||
/// author = "Lewis Carroll"
|
||||
/// translator = "Henri Bué"
|
||||
/// language = { name = "Français", code = "fr" }
|
||||
/// language_name = "Français"
|
||||
///
|
||||
/// [[translations.hu]]
|
||||
/// title = "Alice Csodaországban"
|
||||
/// author = "Lewis Carroll"
|
||||
/// translator = "Kosztolányi Dezső"
|
||||
/// language = { name = "Hungarian", code = "hu" }
|
||||
/// ```
|
||||
pub translations: HashMap<String, Book>,
|
||||
|
||||
|
@ -175,12 +174,12 @@ impl MDBook {
|
|||
/// The `book.toml` file should be in the root directory of the book project.
|
||||
/// The project root directory is the one specified when creating a new `MDBook`
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// # extern crate mdbook;
|
||||
/// # use mdbook::MDBook;
|
||||
/// # use std::path::Path;
|
||||
/// # use std::path::PathBuf;
|
||||
/// # fn main() {
|
||||
/// let mut book = MDBook::new(Path::new("project_root_dir"));
|
||||
/// let mut book = MDBook::new(&PathBuf::from("project_root_dir"));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
|
@ -247,12 +246,6 @@ impl MDBook {
|
|||
/// block will be interpreted as properties of the main book.
|
||||
///
|
||||
/// `project_root` is ignored.
|
||||
///
|
||||
/// - dest_base
|
||||
/// - render_intent
|
||||
/// - template_dir
|
||||
/// - indent_spaces
|
||||
/// - livereload
|
||||
pub fn parse_from_btreemap(&mut self, conf: &BTreeMap<String, toml::Value>) -> &mut Self {
|
||||
|
||||
let mut config = conf.clone();
|
||||
|
@ -398,8 +391,6 @@ impl MDBook {
|
|||
for key in self.translations.clone().keys() {
|
||||
if let Some(mut b) = self.translations.clone().get_mut(key) {
|
||||
|
||||
// TODO error handling could be better here
|
||||
|
||||
let first_as_index = match self.render_intent {
|
||||
RenderIntent::HtmlHandlebars => true,
|
||||
};
|
||||
|
@ -617,76 +608,5 @@ impl MDBook {
|
|||
self
|
||||
}
|
||||
|
||||
// TODO update
|
||||
|
||||
// pub fn test(&mut self) -> Result<(), Box<Error>> {
|
||||
// // read in the chapters
|
||||
// try!(self.parse_summary());
|
||||
// for item in self.iter() {
|
||||
|
||||
// match *item {
|
||||
// BookItem::Chapter(_, ref ch) => {
|
||||
// if ch.path != PathBuf::new() {
|
||||
|
||||
// let path = self.get_src().join(&ch.path);
|
||||
|
||||
// println!("[*]: Testing file: {:?}", path);
|
||||
|
||||
// let output_result = Command::new("rustdoc")
|
||||
// .arg(&path)
|
||||
// .arg("--test")
|
||||
// .output();
|
||||
// let output = try!(output_result);
|
||||
|
||||
// if !output.status.success() {
|
||||
// return Err(Box::new(io::Error::new(ErrorKind::Other, format!(
|
||||
// "{}\n{}",
|
||||
// String::from_utf8_lossy(&output.stdout),
|
||||
// String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// _ => {},
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// /// Returns a flat depth-first iterator over the elements of the book, it returns an [BookItem enum](bookitem.html):
|
||||
// /// `(section: String, bookitem: &BookItem)`
|
||||
// ///
|
||||
// /// ```no_run
|
||||
// /// # extern crate mdbook;
|
||||
// /// # use mdbook::MDBook;
|
||||
// /// # use mdbook::BookItem;
|
||||
// /// # use std::path::Path;
|
||||
// /// # fn main() {
|
||||
// /// # let mut book = MDBook::new(Path::new("mybook"));
|
||||
// /// for item in book.iter() {
|
||||
// /// match item {
|
||||
// /// &BookItem::Chapter(ref section, ref chapter) => {},
|
||||
// /// &BookItem::Affix(ref chapter) => {},
|
||||
// /// &BookItem::Spacer => {},
|
||||
// /// }
|
||||
// /// }
|
||||
// ///
|
||||
// /// // would print something like this:
|
||||
// /// // 1. Chapter 1
|
||||
// /// // 1.1 Sub Chapter
|
||||
// /// // 1.2 Sub Chapter
|
||||
// /// // 2. Chapter 2
|
||||
// /// //
|
||||
// /// // etc.
|
||||
// /// # }
|
||||
// /// ```
|
||||
|
||||
// pub fn iter(&self) -> BookItems {
|
||||
// BookItems {
|
||||
// items: &self.content[..],
|
||||
// current_index: 0,
|
||||
// stack: Vec::new(),
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ impl TocContent {
|
|||
false
|
||||
}
|
||||
|
||||
// TODO update
|
||||
// This doesn't seem to be used.
|
||||
|
||||
// /// This function takes a slice `&[x,y,z]` and returns the corresponding sub-chapter if it exists.
|
||||
// ///
|
||||
|
|
72
src/lib.rs
72
src/lib.rs
|
@ -5,7 +5,7 @@
|
|||
//!
|
||||
//! This is the API doc, but you can find a [less "low-level" documentation here](../index.html) that
|
||||
//! contains information about the command line tool, format, structure etc.
|
||||
//! It is also rendered with mdBook to showcase the features and default theme.
|
||||
//! It is also rendered with mdBook to showcase the features and default html template.
|
||||
//!
|
||||
//! Some reasons why you would want to use the crate (over the cli):
|
||||
//!
|
||||
|
@ -17,57 +17,49 @@
|
|||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```no_run
|
||||
//! Building a book by the path to its directory:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! extern crate mdbook;
|
||||
//!
|
||||
//! use mdbook::MDBook;
|
||||
//! use std::path::Path;
|
||||
//! use mdbook::renderer::HtmlHandlebars;
|
||||
//! use std::path::PathBuf;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let mut book = MDBook::new(Path::new("my-book")) // Path to root
|
||||
//! .set_src(Path::new("src")) // Path from root to source directory
|
||||
//! .set_dest(Path::new("book")) // Path from root to output directory
|
||||
//! .read_config(); // Parse book.json file for configuration
|
||||
//! let path = PathBuf::from("my-book"); // Path to the book project's root
|
||||
//! let renderer = HtmlHandlebars::new();
|
||||
//! try!(renderer.build(&path)); // Build the book
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! book.build().unwrap(); // Render the book
|
||||
//! Or, preparing an `MDBook` struct step-by-step and passing it to a renderer:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! extern crate mdbook;
|
||||
//!
|
||||
//! use mdbook::MDBook;
|
||||
//! use mdbook::renderer::HtmlHandlebars;
|
||||
//! use std::path::PathBuf;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let path = PathBuf::from("my-book"); // Path to the book project's root
|
||||
//! let mut book_project = MDBook::new(&path);
|
||||
//! book_project.read_config(); // Parse book.toml file for configuration
|
||||
//! book_project.parse_books(); // Parse SUMMARY.md, build TOC, parse chapters
|
||||
//! book_project.link_translations(); // Identity links between translations
|
||||
//!
|
||||
//! let renderer = HtmlHandlebars::new();
|
||||
//! try!(renderer.render(&book_project)); // Render the book
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Implementing a new Renderer
|
||||
//!
|
||||
//! If you want to create a new renderer for mdBook, the only thing you have to do is to implement
|
||||
//! the [Renderer trait](renderer/renderer/trait.Renderer.html)
|
||||
//!
|
||||
//! And then you can swap in your renderer like this:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # extern crate mdbook;
|
||||
//! #
|
||||
//! # use mdbook::MDBook;
|
||||
//! # use mdbook::renderer::HtmlHandlebars;
|
||||
//! # use std::path::Path;
|
||||
//! #
|
||||
//! # fn main() {
|
||||
//! # let your_renderer = HtmlHandlebars::new();
|
||||
//! #
|
||||
//! let book = MDBook::new(Path::new("my-book")).set_renderer(Box::new(your_renderer));
|
||||
//! # }
|
||||
//! If you want to create a new renderer for mdBook, implement the [Renderer
|
||||
//! trait](renderer/renderer/trait.Renderer.html), which is composed of two
|
||||
//! functions, `.build()` and `.render()`.
|
||||
//! ```
|
||||
//! If you make a renderer, you get the book constructed in form of `Vec<BookItems>` and you get
|
||||
//! the book config in a `BookConfig` struct.
|
||||
//!
|
||||
//! It's your responsability to create the necessary files in the correct directories.
|
||||
//!
|
||||
//! ## utils
|
||||
//!
|
||||
//! I have regrouped some useful functions in the [utils](utils/index.html) module, like the following function
|
||||
//!
|
||||
//! ```ignore
|
||||
//! utils::fs::create_path(path: &Path)
|
||||
//! ```
|
||||
//! This function creates all the directories in a given path if they do not exist
|
||||
//!
|
||||
//! Make sure to take a look at it.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
|
|
@ -47,8 +47,13 @@ impl Renderer for HtmlHandlebars {
|
|||
|
||||
try!(utils::fs::clean_output_dir(&book_project.get_dest_base()));
|
||||
|
||||
// TODO talk to the user
|
||||
try!(self.render(&book_project));
|
||||
match self.render(&book_project) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
println!("Error: {:#?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#[cfg(test)]
|
||||
|
||||
extern crate tempdir;
|
||||
|
||||
use std;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use utils;
|
||||
use utils::fs::copy_files_except_ext;
|
||||
|
||||
#[test]
|
||||
fn it_copies_data_file() {
|
||||
|
@ -79,3 +82,65 @@ fn it_doesnt_delete_toplevel_dotfiles() {
|
|||
fs::remove_dir_all(dest_base);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_files_except_ext_test() {
|
||||
let tmp = match tempdir::TempDir::new("") {
|
||||
Ok(t) => t,
|
||||
Err(_) => panic!("Could not create a temp dir"),
|
||||
};
|
||||
|
||||
// Create a couple of files
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("file.txt")) {
|
||||
panic!("Could not create file.txt")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("file.md")) {
|
||||
panic!("Could not create file.md")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("file.png")) {
|
||||
panic!("Could not create file.png")
|
||||
}
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir")) {
|
||||
panic!("Could not create sub_dir")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir/file.png")) {
|
||||
panic!("Could not create sub_dir/file.png")
|
||||
}
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir_exists")) {
|
||||
panic!("Could not create sub_dir_exists")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) {
|
||||
panic!("Could not create sub_dir_exists/file.txt")
|
||||
}
|
||||
|
||||
// Create output dir
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("output")) {
|
||||
panic!("Could not create output")
|
||||
}
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) {
|
||||
panic!("Could not create output/sub_dir_exists")
|
||||
}
|
||||
|
||||
match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) {
|
||||
Err(e) => panic!("Error while executing the function:\n{:?}", e),
|
||||
Ok(_) => {},
|
||||
}
|
||||
|
||||
// Check if the correct files where created
|
||||
if !(&tmp.path().join("output/file.txt")).exists() {
|
||||
panic!("output/file.txt should exist")
|
||||
}
|
||||
if (&tmp.path().join("output/file.md")).exists() {
|
||||
panic!("output/file.md should not exist")
|
||||
}
|
||||
if !(&tmp.path().join("output/file.png")).exists() {
|
||||
panic!("output/file.png should exist")
|
||||
}
|
||||
if !(&tmp.path().join("output/sub_dir/file.png")).exists() {
|
||||
panic!("output/sub_dir/file.png should exist")
|
||||
}
|
||||
if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() {
|
||||
panic!("output/sub_dir/file.png should exist")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ pub fn copy_data_file(src_path: &str, dest_path: &Path) -> Result<(), Box<Error>
|
|||
/// I.e. "data/_html-template/css/book.css" will be written to
|
||||
/// "assets/css/book.css".
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// utils::fs::copy_data("data/_html-template/**/*",
|
||||
/// "data/_html-template/",
|
||||
/// vec!["data/_html-template/_*"],
|
||||
|
@ -378,78 +378,3 @@ pub fn create_gitignore(proj: &MDBook) {
|
|||
|
||||
f.write_all(&text.into_bytes()).expect("Could not write to file.");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// tests
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate tempdir;
|
||||
|
||||
use super::copy_files_except_ext;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn copy_files_except_ext_test() {
|
||||
let tmp = match tempdir::TempDir::new("") {
|
||||
Ok(t) => t,
|
||||
Err(_) => panic!("Could not create a temp dir"),
|
||||
};
|
||||
|
||||
// Create a couple of files
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("file.txt")) {
|
||||
panic!("Could not create file.txt")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("file.md")) {
|
||||
panic!("Could not create file.md")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("file.png")) {
|
||||
panic!("Could not create file.png")
|
||||
}
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir")) {
|
||||
panic!("Could not create sub_dir")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir/file.png")) {
|
||||
panic!("Could not create sub_dir/file.png")
|
||||
}
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir_exists")) {
|
||||
panic!("Could not create sub_dir_exists")
|
||||
}
|
||||
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) {
|
||||
panic!("Could not create sub_dir_exists/file.txt")
|
||||
}
|
||||
|
||||
// Create output dir
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("output")) {
|
||||
panic!("Could not create output")
|
||||
}
|
||||
if let Err(_) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) {
|
||||
panic!("Could not create output/sub_dir_exists")
|
||||
}
|
||||
|
||||
match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) {
|
||||
Err(e) => panic!("Error while executing the function:\n{:?}", e),
|
||||
Ok(_) => {},
|
||||
}
|
||||
|
||||
// Check if the correct files where created
|
||||
if !(&tmp.path().join("output/file.txt")).exists() {
|
||||
panic!("output/file.txt should exist")
|
||||
}
|
||||
if (&tmp.path().join("output/file.md")).exists() {
|
||||
panic!("output/file.md should not exist")
|
||||
}
|
||||
if !(&tmp.path().join("output/file.png")).exists() {
|
||||
panic!("output/file.png should exist")
|
||||
}
|
||||
if !(&tmp.path().join("output/sub_dir/file.png")).exists() {
|
||||
panic!("output/sub_dir/file.png should exist")
|
||||
}
|
||||
if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() {
|
||||
panic!("output/sub_dir/file.png should exist")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue