Tomorrow Never Dies

***Tomorrow Never Dies*** is a 1997 spy film and the eighteenth in the *James Bond* series to be produced by Eon Productions, and the second to star Pierce Brosnan as the fictional MI6 agent James Bond. Directed by Roger Spottiswoode, with the screenplay written by Bruce Feirstein, the film follows Bond as he attempts to stop Elliot Carver, a power-mad media mogul, from engineering world events to initiate World War III.

The film was produced by Michael G. Wilson and Barbara Broccoli, and was the first *James Bond* film made after the death of producer Albert R. Broccoli, to whom the film pays tribute in the end credits, as well as the last one to be officially released under the United Artists label. Filming locations included France, Thailand, Germany, Mexico and the United Kingdom. *Tomorrow Never Dies* performed well at the box office, grossing over $333 million worldwide, becoming the fourth-highest-grossing film of 1997 and earned a Golden Globe nomination despite mixed reviews. While its performance at the U.S. box office surpassed that of its predecessor, *GoldenEye*, it was the only one of Pierce Brosnan's Bond films not to open at number one at the box office, as it opened the same day as *Titanic*, and finished at number two that week.

## Plot

MI6 sends James Bond, agent 007, into the field to reconnoiter a terrorist arms bazaar on the Russian border. Despite M's insistence on letting 007 finish his reconnaissance, Royal Navy Admiral Roebuck orders the frigate HMS *Chester* to fire a Harpoon missile at the bazaar. Bond then discovers two nuclear torpedoes mounted on an L-39 Albatros, and is forced to pilot the L-39 away seconds before the bazaar is destroyed by the missile because the missile is out of range to be aborted.

Media baron Elliot Carver starts his plans to use an encoder obtained at the bazaar by his henchman, cyberterrorist Henry Gupta, to provoke war between China and the UK. Meaconing the GPS signal using the encoder, Gupta sends the frigate HMS *Devonshire* off-course into Chinese-occupied waters in the South China Sea, where Carver's stealth ship, commanded by Mr. Stamper, ambushes it – sinking it in the process – and steals one of its missiles, while shooting down a Chinese MiG fighter jet investigating the scene and killing off the *Devonshire*'s survivors with weaponry loaded with Chinese ammunition. The British Minister of Defence orders Roebuck to deploy the fleet to investigate the sinking of the frigate, and demanding retaliation, leaving M only 48 hours to investigate its sinking and avert a war.

M sends Bond to investigate Carver because MI6 became suspicious of him after he releases news articles about the crisis hours before MI6 had learned of it. Bond travels to Hamburg to seduce Carver's wife, Paris (an ex-girlfriend of Bond's), to get information that would help him enter Carver's newspaper headquarters. He defeats three of Stamper's men and cuts Carver off the air during the inaugural broadcast of his satellite network. Carver then discovers the truth about Paris and Bond and orders both of them killed. Meanwhile, at Bond's hotel room, he and Paris reconcile. The next day, she provides him the information he needs to infiltrate Carver's newspaper factory to recover the GPS encoder. Bond goes to the newspaper factory and steals the encoder. While he is gone, Carver's assassin Dr. Kaufman kills Paris. After Bond returns to find Paris' body, Kaufman attempts to shoot him. Bond is able to kill Kaufman and escape, protecting the encoder.

At a U.S. Air Force base in Okinawa, Bond learns that the encoder had been tampered with, and goes to the South China Sea to investigate the wreck (which was actually in Vietnamese waters). He and Wai Lin, a Chinese Ministry of State Security agent on the same case, explore the sunken ship and discover one of its cruise missiles missing, but after reaching the surface they are captured by Stamper and taken to the CMGN tower in Saigon. They soon escape and decide to collaborate on the investigation. The two contact the Royal Navy and the People's Liberation Army Air Force to explain Carver's scheme; Carver plans to destroy most of the Chinese government with the stolen missile, allowing a corrupt Chinese general to step in and negotiate a truce between Britain and China, both of which will have waged a naval war. Once the conflict is over, Carver will be given exclusive broadcasting rights in China for the next century. Finding Carver's stealth ship, they board it to prevent him from firing the missile at Beijing.

During the attempt, Wai Lin is captured, forcing Bond to devise a second plan. Bond captures Gupta to use as his own hostage, but Carver kills Gupta claiming he has "outlived his contract." Bond detonates an explosive, damaging the ship and rendering it visible to the Chinese and British navies' radars, and vulnerable to a subsequent Royal Navy attack. While Wai Lin disables the engines, she is recaptured by Stamper. Bond kills Carver with his own sea drill and attempts to destroy the warhead with detonators, but Stamper attacks him, and sends a chained Wai Lin into the water. Bond traps Stamper in the missile firing mechanism and saves Wai Lin as the missile explodes, destroying the ship and killing Stamper. Bond and Wai Lin share a romantic moment amidst the wreckage as HMS *Bedford* searches for them.

## Cast

* Pierce Brosnan as James Bond, MI6 agent 007.
* Jonathan Pryce as Elliot Carver, a media mogul with scant regard for any of his actions taken as a result of his media ambition.
* Michelle Yeoh as Colonel Wai Lin, a skilled Chinese spy and Bond's ally.
* Teri Hatcher as Paris Carver, a former girlfriend of Bond who is now Carver's trophy wife.
* Götz Otto as Richard Stamper, Carver's henchman, who is skilled in the art of Chakra torture.
* Ricky Jay as Henry Gupta, an American "Techno-terrorist" in the employ of Carver.
* Joe Don Baker as Jack Wade, CIA liaison, reprising his role from *GoldenEye*.
* Vincent Schiavelli as Dr. Kaufman, a professional assassin used by Elliot Carver.
* Judi Dench as M, reprising her role from *GoldenEye*.
* Desmond Llewelyn as Q.
* Samantha Bond as Miss Moneypenny.
* Daphne Deckers as PR person of Carver Media Group Network.
* Geoffrey Palmer as Admiral Roebuck, M's Royal Navy colleague.
* Colin Salmon as Charles Robinson, M's Chief of Staff.
* Julian Fellowes as the British Minister of Defence.
* Philip Kwok as General Chang, Chinese general who is helping Carver start a war between China and Britain.
* Cecilie Thomsen as Professor Inga Bergstrom.
* Gerard Butler and Julian Rhind-Tutt as crewmen of HMS *Devonshire*.
* Michael Byrne as Admiral Kelly, commander of the Royal Navy task force sent to the South China Sea.

## Production

*Bond 18* was greenlit after the positive public reception to the teaser trailer for *GoldenEye* in May 1995. Following the success of *GoldenEye* in reviving the Bond series, there was pressure to recreate that success in the film's follow-up production. This pressure came from MGM which, along with its new owner, billionaire Kirk Kerkorian, wanted the film's release to coincide with their public stock offering. Co-producer Michael G. Wilson also expressed concern regarding the public's expectations subsequent to the success of *GoldenEye*, commenting: "You realize that there's a huge audience and I guess you don't want to come out with a film that's going to somehow disappoint them." This was the first Bond film to be made after the death of Albert R. Broccoli, who had been involved with the series' production since its beginning; the film is dedicated to his memory. The rush to complete the film drove the budget to $110 million. The producers were unable to persuade Martin Campbell, the director of *GoldenEye* who choose direct *The Mask of Zorro*, to return; his agent said that "Martin just didn't want to do two Bond films in a row." Instead, Roger Spottiswoode was chosen in September 1996. Spottiswoode had previously offered to direct *GoldenEye* while Timothy Dalton was still in the leading role.

### Writing

As had been the case with several previous films in the series, an entirely original story was required as there remained no Ian Fleming novels or stories to adapt. The scriptwriting process was finished very late due to lengthy disputes.[*citation needed*]

Initial writers on the project included John Cork, Richard Smith, and novelist Donald E. Westlake. In 1995, Westlake wrote two story treatments in collaboration with Wilson, both of which featured a villain who plans to destroy Hong Kong with explosives on the eve of the city's July 1997 transfer of sovereignty to China. Westlake used some of his ideas for a novel he completed the next year, though it was not published until 2017 under the title *Forever and a Death*. Director Roger Spottiswoode said that, in January 1997, MGM had a script that was also focused on the Hong Kong handover; however, this plot could not be used for a film opening at the end of the year, so they had to start "almost from scratch at T-minus zero!"

Bruce Feirstein, who had worked on *GoldenEye*, wrote the initial script. Feirstein claimed that his inspiration was his own experience working with journalism and viewing both Sky News and CNN's 24-hour news coverage of the Gulf War, stating that he aimed to "write something that was grounded in a nightmare of reality." Feirstein's script was then passed to Spottiswoode, who gathered seven Hollywood screenwriters in London to brainstorm, eventually choosing Nicholas Meyer to perform rewrites. The script was also worked on by Dan Petrie, Jr. and David Campbell Wilson before Feirstein was brought in for a final polish. Although Feirstein retained sole writing credit in the film and in the advertising, Meyer, Petrie and Wilson were given credit with Feirstein on the title page of the film's novelization by Raymond Benson. While many reviewers compared Elliot Carver to Rupert Murdoch, Feirstein based the character on Robert Maxwell, with Carver's reported death bearing similarities to that of Maxwell's.

Wilson stated, "We didn't have a script that was ready to shoot on the first day of filming", while Pierce Brosnan said, "We had a script that was not functioning in certain areas."

The title was inspired by the Beatles' song "Tomorrow Never Knows". The eventual title came about by accident: one of the potential titles was *Tomorrow Never Lies* (referring to the *Tomorrow* newspaper in the plot), and this was faxed to MGM. However, due to a typing error, this became *Tomorrow Never Dies*, a title which MGM found so attractive that they insisted on using it. The title was the first not to have any relation to Fleming's life or work.

### Casting

Teri Hatcher was three months pregnant when shooting started, although her publicist stated the pregnancy did not affect the production schedule. Hatcher later regretted playing Paris Carver, saying "It's such an artificial kind of character to be playing that you don't get any special satisfaction from it." Actress Sela Ward had auditioned for the role, but lost out, reportedly being told the producers wanted her but ten years younger. Hatcher, at 32, was seven years Ward's junior and was in the midst of playing Lois Lane on the television show *Lois & Clark: The New Adventures of Superman* for which she had been voted the "Sexiest Woman on Television" by readers of *TV Guide* the previous year. According to Brosnan, Monica Bellucci also screen tested for the role but as Brosnan remarked, "the fools said no." Daphne Deckers, who portrays the PR woman, also confirms that she saw Bellucci the same day she herself auditioned. Bellucci would later go on to play a role in the 24th Bond film, *Spectre*.

The role of Elliot Carver was initially offered to Anthony Hopkins (who also had been offered a role in *GoldenEye*), but he declined in favour of *The Mask of Zorro*.

Natasha Henstridge was rumoured as cast in the lead Bond Girl role, but eventually, Yeoh was confirmed in that role. Brosnan was impressed, describing her as a "wonderful actress" who was "serious and committed about her work". She reportedly wanted to perform her own stunts, but was prevented because director Spottiswoode ruled it too dangerous and prohibited by insurance restrictions.

When Götz Otto was called in for casting, he was given twenty seconds to introduce himself; his hair had recently been cropped short for a TV role. Saying, "I'm big, I'm bad, and I'm German", he did it in five. Template:Better source needed

### Filming

 File:Tomorrow Never Dies - BMW 750iL.jpg
With Vic Armstrong directing the second unit, filming of the $11 million 4-minute pre-title sequence began on 18 January 1997 at Peyresourde-Balatestas Airport, Peyragudes in the French Pyrenees. The plane Bond is seen to purloin in the movie was a Czech-built Aero Vodochody L-39ZO Albatros weapons jet trainer, supplied by a British company and flown by stunt pilots Tony "Taff" Smith and Mark (son of Ray) Hanna. After completing work in France, the second unit moved on to Portsmouth to film the scenes where the Royal Navy prepares to engage the Chinese, with Template:HMS standing in for the various fictional Type 23 Frigates in the story. The main unit began filming on 1 April. They were unable to use Leavesden Studios, which they had constructed from an abandoned Rolls-Royce factory for *GoldenEye*, as George Lucas was using it for *Star Wars: Episode I – The Phantom Menace*, so instead they constructed sound stages in another derelict industrial site nearby. They also used the 007 Stage at Pinewood Studios. The scene at the "U.S. Air Base in the South China Sea" where Bond hands over the GPS encoder was actually filmed in the area known as Blue Section at RAF Lakenheath. The sea landing used the vast tank built for *Titanic* in Rosarito, Baja California. The MH-53J in the film was from the US Air Force's 352d Special Operations Group at RAF Mildenhall.

Some scenes were planned to be filmed on location in Ho Chi Minh City, and the production had been granted a visa. It would have been the first major film to be shot in Vietnam since the Vietnam War. However, the visa was later rescinded by Vietnamese Prime Minister Võ Văn Kiệt two months after planning had begun, forcing filming to move to Bangkok. Bond spokesman Gordon Arnell claimed the Vietnamese were unhappy with crew and equipment needed for pyrotechnics, with a Vietnamese official saying it was due to "many complicated reasons". Anthony Waye says he believed the decision was caused after Vietnam's Communist government viewed the opening credits of *GoldenEye*, which featured "semi-naked ladies smashing up hammer-and-sickle emblems with sledgehammers, illustrating the fall of communism." Two locations from previous Bond films were used: Brosnan and Hatcher's love scene was filmed at Stoke Park, which had been featured in *Goldfinger*, and the bay where they search for Carver's stealth boat is Phang Nga Bay, previously used for *The Man with the Golden Gun*.

Spottiswoode tried to innovate in the action scenes. Since the director felt that after the tank chase in *GoldenEye* he could not use a bigger vehicle, a scene with Bond and Wai Lin on a BMW motorcycle was created. Another innovation was the remote-controlled car, which had no visible driver – an effect achieved by adapting a BMW 750i to put the steering wheel on the back seat. The car chase sequence with the 750i took three weeks to film, with Brent Cross car park being used to simulate Hamburg – although the final leap was filmed on location. A stunt involving setting fire to three vehicles produced more smoke than anticipated, causing a member of the public to call the fire brigade. The upwards camera angle filming the HALO jump created the illusion of having the stuntman opening its parachute close to the water.

Spottiswoode did not return to direct the next film; he said the producers asked him, but he was too tired. Brosnan and Hatcher were reported to have feuded briefly during filming due to her arriving late onto the set one day. The matter was quickly resolved, though, and Brosnan apologised to Hatcher after realising she was pregnant and was late for that reason.

*Tomorrow Never Dies* marked the first appearance of the Walther P99 as Bond's pistol. It replaced the Walther PPK that the character had carried in every Eon Bond film since *Dr. No* in 1962, with the exception of *Moonraker* in which Bond was not seen with a pistol. Walther wanted to debut its new firearm in a Bond film, which had been one of its most visible endorsers. Previously, the P5 was introduced in *Octopussy*. Bond would use the P99 until Daniel Craig reverted to the PPK as 007 in *Quantum of Solace* in 2008.

### Music

*Main article: Tomorrow Never Dies (soundtrack)*
Prolific composer John Barry was in talks to return to the James Bond films for the first time in a decade but could not reach an agreement over his fee according to his then-agent Richard Kraft. Barbara Broccoli subsequently chose David Arnold to score *Tomorrow Never Dies* on a recommendation from Barry. Arnold had come to Barry's attention through his successful cover interpretations in *Shaken and Stirred: The David Arnold James Bond Project*, which featured major artists performing the former James Bond title songs in new arrangements. Arnold said that his score aimed for "a classic sound but [with] a modern approach", combining techno music with a recognisably Barry-inspired "classic Bond" sound—notably Arnold borrowed from Barry's score for *From Russia with Love*. The score was done across a period of six months, with Arnold writing music and revising previous pieces as he received edited footage of the film. The music for the indoor car chase sequence was co-written with the band Propellerheads, who had worked with Arnold on *Shaken and Stirred*. The soundtrack was well received by critics with Christian Clemmensen of Filmtracks describing it as "an excellent tribute to the entire series of Bond score".

At first, the theme song was to be written by Arnold himself, with the help of lyricist Don Black and singer-songwriter David McAlmont, who recorded the demo. However, MGM wanted a more popular artist, and invited various singers to write songs before one was picked through a competitive process. There were around twelve submissions, including songs from Swan Lee, Pulp, Saint Etienne, Marc Almond, and Sheryl Crow. Crow's song was chosen for the main titles. Arnold's composition, "Surrender", performed by k.d. lang, was still used for the end titles, and features the same prominent melodic motif as the film's score. This was the fourth Bond film to have different opening and closing songs. Pulp's effort was re-titled as "Tomorrow Never Lies" and appeared as a b-side on their 1997 single "Help The Aged". The original "Tomorrow Never Dies" rough mix of the song was eventually released on the bonus disc of the *This Is Hardcore* deluxe edition in 2006. Moby created a remake of the "James Bond Theme" to be used for the movie. Two different versions of the soundtrack album were released, the first featuring only music from the first half of the film, and the second rectifying this but cutting several tracks, including the songs, to make room for the missing score tracks.

## Release and reception

The film had a World Charity Premiere at The Odeon Leicester Square, on 9 December 1997; this was followed by an after premiere party at Bedford Square, home of original Ian Fleming publisher, Jonathan Cape. The film went on general release in the UK and Ireland on 12 December and in most other countries during the following week. It opened at number 2 in the US, with $25,143,007 from 2,807 cinemas – average of $8,957 per cinema – behind *Titanic*, which would become the highest-grossing film of all time up to that point. *Tomorrow Never Dies* ultimately achieved a worldwide gross of over $330 million, although it did not surpass its predecessor *GoldenEye*, which grossed almost $20 million more.

Rotten Tomatoes gave the film a 57% rating based on 77 reviews. The site's consensus states: "A competent, if sometimes by-the-numbers entry to the 007 franchise, *Tomorrow Never Dies* may not boast the most original plot but its action sequences are genuinely thrilling." On Metacritic the film has a score of 52% based on reviews from 38 critics, indicating "mixed or average reviews". Audiences polled by CinemaScore gave the film an average grade of "A-" on an A+ to F scale.

In the *Chicago Sun-Times*, Roger Ebert gave the film three out of four-stars, saying "*Tomorrow Never Dies* gets the job done, sometimes excitingly, often with style" with the villain "slightly more contemporary and plausible than usual", bringing "some subtler-than-usual satire into the film". James Berardinelli described it as "the best Bond film in many years" and said Brosnan "inhabits his character with a suave confidence that is very like Connery's." Kenneth Turan of the *Los Angeles Times*, thought a lot of *Tomorrow Never Dies* had a "stodgy, been-there feeling", with little change from previous films. Charles Taylor wrote for Salon.com that the film was "a flat, impersonal affair".

The title song sung by Sheryl Crow was nominated for a Golden Globe for "Best Original Song – Motion Picture" and a Grammy for "Best Song Written Specifically for a Motion Picture or for Television". The film received four nominations for Saturn Awards, with Brosnan winning "Best Actor". It also won a MPSE Golden Reel Award for "Best Sound Editing – Foreign Feature" and a BMI Film Music Award.

The original UK release received various cuts in scenes of violence and martial arts weaponry, to reduce the impact of sound effects and to receive a more box-office-friendly 12 certificate. Further cuts were made to the video/DVD release to retain this rating. These edits were restored for the Ultimate Edition DVD release in the UK, which was consequently upgraded to a 15 certificate. However, upon the release of the Blu-ray in 2012, it was rated back down to a 12 uncut.

### Retrospective reviews

In the wake of its original release, critics and audiences have praised *Tomorrow Never Dies* for its prescience. Den of Geek, on the film's twentieth anniversary, observed of the film's plot: "It's an improbable set-up which was likely intended as a satire of Murdoch’s unaccountable media empire, but the risks of such technological manipulation have since proved to be frighteningly plausible." Den of Geek also highlights that "technology wasn't the only modern danger to be pre-empted by *Tomorrow Never Dies* – it also offers a revealing peek into the confused state of the British national psyche, which might help to explain the country's ongoing Brexit debates." Similarly, *HeadStuff* highlights its relevance in 2020, noting that "some modern critics argue that Carver's emphasis on traditional journalism date the film and that if the Internet existed to such an extent as it does twenty years later, his plan would be instantly foiled... not really sure those people have been following current events over the past two years." "Far Out Magazine" highlighted Brosnan's performance, when his Bond happens upon the deceased Paris Carver in his hotel room: "There’s more substance here in a four-minute encounter than Brosnan found over four whole films."

## Appearances in other media

*Tomorrow Never Dies* was the first of three Bond films to be adapted into books by then-current Bond novelist Raymond Benson. Benson's version is expanded from the screenplay including additional scenes with Wai Lin and other supporting characters not in the film. The novel traces Carver's background as the son of media mogul Lord Roverman, whom Carver blackmails into suicide, later taking over his business. The novel also attempts to merge Benson's series with the films, particularly by continuing a middle-of-the-road approach to John Gardner's continuity. Notably it includes a reference to the film version of *You Only Live Twice* where he states that Bond was lying to Miss Moneypenny when he said he had taken a course in Asian languages. *Tomorrow Never Dies* also mentions Felix Leiter, although it states that Leiter had worked for Pinkertons Detective Agency, which is thus exclusive to the literary series. Subsequent Bond novels by Benson were affected by *Tomorrow Never Dies*, specifically Bond's weapon of choice being changed from the Walther PPK to the Walther P99. Benson said in an interview that he felt *Tomorrow Never Dies* was the best of the three novelisations he wrote.

The film was also adapted into a third-person shooter PlayStation video game, *Tomorrow Never Dies*. The game was developed by Black Ops and published by Electronic Arts on 16 November 1999. Game Revolution described it as "really just an empty and shallow game", and IGN said it was "mediocre".

## See also

* Outline of James Bond
* Sea Shadow (IX-529), the boat that inspired the design of Carver's stealth boat

**Lua error: bad argument #2 to 'title.new' (unrecognized namespace name 'Portal').**

## References

1. ↑ Tomorrow never Dies. European Audiovisual Observatory.
2. ↑ Tomorrow Never Dies (1997).
3. ↑ "James Bond Vs. Himself". Box Office Mojo. Retrieved 1 January 2015.
4. ↑ Weekend Box Office Results for December 19-21, 1997 - Box Office Mojo.
5. ↑ 20 Things You Didn't Know About Tomorrow Never Dies (1997) (7 February 2021).
6. ↑ 6.0 6.1 6.2 6.3 6.4 Field, Matthew (2015). *Some kind of hero : 007 : the remarkable story of the James Bond films*. ISBN 978-0-7509-6421-0.
7. ↑ 7.0 7.1 7.2 7.3 Ashton, Richard (1997). Tomorrow Never Dies. hmss.com.
8. ↑ Business Data for Tomorrow Never Dies. IMDb (19 December 1997).
9. ↑ "It comes from not growing up at all" | Tomorrow Never Dies (1997) (13 May 2018).
10. ↑ 5 Films You Might Not Realize Were Directed by Martin Campbell | High-Def Digest.
11. ↑ 11.0 11.1 11.2 11.3 "MGM's Completion Bond", *Variety*, 30 December 1996.
12. ↑ "Pierce Brosnan returns as James Bond, battling British baddie Jonathan Pryce". *Cinefantastique*. 1997. "Director Roger Spottiswoode said, "I'd met the Broccoli family years before about the possibility of making one of the [prior Bond] Timothy Dalton episodes".
13. ↑ Poggiali, Philip. "Fall of the City: Bond 18 and Westlake." *MI6 Confidential* 32 (2015): 22-26.
14. ↑ 14.0 14.1 14.2 Collette, Kevin. "Yesterday's 'Tomorrow': Spottiswoode Interview", ianfleming.org, 10 April 2004.
15. ↑ Ferrante, Anthony C.. "The Man with the Golden Pen", Eon Magazine, October 1999.
16. ↑ 16.0 16.1 16.2 16.3 Production Notes – Tomorrow Never Dies. MI6-HQ.com.
17. ↑ 17.0 17.1 17.2 Bruce Feirstein. "Bruce Feirstein: The Tao of Bond-Film Naming", *Vanity Fair*, 29 January 2008.
18. ↑ Johns, Elizabeth. "Teri Hatcher Pregnant", E!, 2 May 1997.
19. ↑ "Live and let lie?", Yahoo! News, 28 November 2008.
20. ↑ Rorke, Robert. "CSI: Sela", *New York Post*, 8 May 2011.
21. ↑ 21.0 21.1 Rebello, Stephen (December 2005). "Playboy Interview: Pierce Brosnan". *Playboy* **52** (12): 61–62+65–68+70. http://commanderbond.net/2994/brosnan-bares-all-for-playboy.html.
22. ↑ Daphne Deckers: 'Ik at sateetjes met James Bond' (nl) (27 October 2015).
23. ↑ Ferguson, Amy. "Back in Action", Tribute.
24. ↑ Cohen, David. "Bond girl Yeoh gets licence to thrill 007", *South China Morning Post*, 11 February 1997.
25. ↑ "Bond Leading Lady Won't Do Stunts", 21 May 1997.
26. ↑ "Much More Than Just A Bond Girl", *South China Morning Post*, 30 May 1997.
27. ↑ "Promi-Porträt: Götz Otto", kwick!, 20 October 2007.
28. ↑ 28.0 28.1 Template:Cite episode
29. ↑ Lande, David (September 2008). Live and Let Fly.
30. ↑ Maskel, Rebecca (13 July 2008). The Airplanes of James Bond.
31. ↑ Real Aeroplane Company.
32. ↑ 32.0 32.1 32.2 Tomorrow Never Dies filming locations. movielocations.com.
33. ↑ Rush and Molloy. "China Resists Western Efforts to Bond", *Daily News*, 10 March 1997.
34. ↑ Template:Cite video
35. ↑ Keeling, Judith. "Bond Goes Down A Bomb in Brent Cross", Evening Standard, 17 June 1997.
36. ↑ Template:Cite video
37. ↑ Kraft, Richard (22 May 2016). THE BEYONDNESS OF THINGS: MY BITTERSWEET RELATIONSHIP WITH BARRY. JOHN BARRY. (en-GB).
38. ↑ Template:Cite video
39. ↑ Burlingame, Jon. "Bonding With the Score", *Los Angeles Times*, 18 December 1997.
40. ↑ 40.0 40.1 Review of Original Album. *filmtracks.com*.
41. ↑ Burlingame, Jon (2012). "5: Casino Royale (1967)", *The Music of James Bond*. Oxford University Press, page 211–3. ISBN 978-0199986767.
42. ↑ Template:Cite video
43. ↑ Tomorrow Never Dies – The Premiere & Press. MI6-HQ.com (12 December 2003).
44. ↑ Release dates for Tomorrow Never Dies. IMDb.
45. ↑ Tomorrow Never Dies (1997) - Box Office Mojo.
46. ↑ GoldenEye. Box Office Mojo.
47. ↑ Tomorrow Never Dies (1997). *Rotten Tomatoes*.
48. ↑ Tomorrow Never Dies. *metacritic.com*.
49. ↑ TOMORROW NEVER DIES (1997) A-. *CinemaScore*.
50. ↑ Ebert, Roger. "Tomorrow Never Dies", *Chicago Sun-Times*, 19 December 1997.
51. ↑ Berardinelli, James. "Tomorrow Never Dies", *reelviews.net*.
52. ↑ Turan, Kenneth. "James Bond Is Back in Franchise That Never Dies", *Los Angeles Times*, 19 December 1997.
53. ↑ Taylor, Charles. "Stale Bonding", *Salon.com*, 19 December 1997.
54. ↑ Awards for Tomorrow Never Dies. IMDb.
55. ↑ TOMORROW NEVER DIES rated 15 by the BBFC.
56. ↑ TOMORROW NEVER DIES - British Board of Film Classification.
57. ↑ James Bond Censor Cuts: Tomorrow Never Dies.
58. ↑ The fascinating prescience of Tomorrow Never Dies (en).
59. ↑ 20 Years On | How Tomorrow Never Dies Injected Fun Into the Brosnan Era of Bond (19 December 2017).
60. ↑ Far Out Magazine, An Exploration of Pierce Brosnan as James Bond: An actor who forged his own path.
61. ↑ Template:Cite interview
62. ↑ " Benson on Bond". The International Association of Media Tie-In Writers. --[[ ----------------------------------

Lua module implementing the Template loop detected: Template:Webarchive template.

A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives

]]

--[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------
]]

require('Module:No globals');
local getArgs = require ('Module:Arguments').getArgs;

--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]

local categories = {}; -- category names
local config = {}; -- global configuration settings
local digits = {}; -- for i18n; table that translates local-wiki digits to western digits
local err\_warn\_msgs = {}; -- error and warning messages
local excepted\_pages = {};
local month\_num = {}; -- for i18n; table that translates local-wiki month names to western digits
local prefixes = {}; -- service provider tail string prefixes
local services = {}; -- archive service provider data from
local s\_text = {}; -- table of static text strings used to build final rendering
local uncategorized\_namespaces = {}; -- list of namespaces that we should not categorize
local uncategorized\_subpages = {}; -- list of subpages that should not be categorized

--[[--------------------------< P A G E S C O P E I D E N T I F I E R S >----------------------------------
]]

local non\_western\_digits; -- boolean flag set true when data.digits.enable is true
local this\_page = mw.title.getCurrentTitle();

local track = {}; -- Associative array to hold tracking categories
local ulx = {}; -- Associative array to hold template data

--[[--------------------------< S U B S T I T U T E >----------------------------------------------------------

Populates numbered arguments in a message string using an argument table.

]]

local function substitute (msg, args)
 return args and mw.message.newRawMessage (msg, args):plain() or msg;
end

--[[--------------------------< tableLength >-----------------------

Given a 1-D table, return number of elements

]]

local function tableLength(T)
 local count = 0
 for \_ in pairs(T) do count = count + 1 end
 return count
end

--[=[-------------------------< M A K E \_ W I K I L I N K >----------------------------------------------------

Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only
link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an
empty string.

]=]

local function make\_wikilink (link, display, no\_link)
 if nil == no\_link then
 if link and ( *~= link) then*
 if display and ( *~= display) then*
 return table.concat ({'', display, ''});
 else
 return table.concat ({'', link, ''});
 end
 end
 return display or *; -- link not set so return the display text*

 else -- no\_link
 if display and ( *~= display) then -- if there is display text*
 return display; -- return that
 else
 return link or *; -- return the target article name or empty string*
 end
 end
end

--[[--------------------------< createTracking >-----------------------

Return data in track[] ie. tracking categories

]]

local function createTracking()
 if not excepted\_pages[this\_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s))
 if uncategorized\_namespaces[this\_page.nsText] then
 return *; -- this page not to be categorized so return empty string*
 end
 for \_,v in ipairs (uncategorized\_subpages) do -- cycle through page name patterns
 if this\_page.text:match (v) then -- test page name against each pattern
 return *; -- this subpage type not to be categorized so return empty string*
 end
 end
 end

 local out = {};
 if tableLength(track) > 0 then
 for key, \_ in pairs(track) do -- loop through table
 table.insert (out, make\_wikilink (key)); -- and convert category names to links
 end
 end
 return table.concat (out); -- concat into one big string; empty string if table is empty

end

--[[--------------------------< inlineError >-----------------------

Critical error. Render output completely in red. Add to tracking category.

This function called as the last thing before abandoning this module

]]

local function inlineError (msg, args)
 track[categories.error] = 1
 return table.concat ({
 'Error in ', -- open the error message span
 config.tname, -- insert the local language template name
 ' template: ',
 substitute (msg, args), -- insert the formatted error message
 '.', -- close the span
 createTracking() -- add the category
 })
end

--[[--------------------------< inlineRed >-----------------------

Render a text fragment in red, such as a warning as part of the final output.
Add tracking category.

 ]]

local function inlineRed(msg, trackmsg)
 if trackmsg == "warning" then
 track[categories.warning] = 1;
 elseif trackmsg == "error" then
 track[categories.error] = 1;
 end

 return '' .. msg .. ''
end

--[[--------------------------< base62 >-----------------------

Convert base-62 to base-10
Credit: https://de.wikipedia.org/wiki/Modul:Expr

]]

local function base62( value )
 local r = 1 -- default return value is input value is malformed

 if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z]
 return; -- nil return when value contains extraneous characters
 end

 local n = #value -- number of characters in value
 local k = 1
 local c
 r = 0
 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit
 c = value:byte( i, i )
 if c >= 48 and c <= 57 then -- character is digit 0-9
 c = c - 48
 elseif c >= 65 and c <= 90 then -- character is ascii a-z
 c = c - 55
 else -- must be ascii A-Z
 c = c - 61
 end
 r = r + c \* k -- accumulate this base62 character's value
 k = k \* 62 -- bump for next
 end -- for i

 return r
end

--[[--------------------------< D E C O D E \_ D A T E >--------------------------------------------------------

Given a date string, return it in iso format along with an indicator of the date's format. Except that month names
must be recognizable as legitimate month names with proper capitalization, and that the date string must match one
of the recognized date formats, no error checking is done here; return nil else

]]

local function decode\_date (date\_str)
 local patterns = {
 ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages
 ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'},
 ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis
 };

 local t = {};

 if non\_western\_digits then -- this wiki uses non-western digits?
 date\_str = mw.ustring.gsub (date\_str, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 if date\_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form
 return date\_str, 'iso';
 end

 for k, v in pairs (patterns) do
 local c1, c2, c3 = mw.ustring.match (date\_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold

 if c1 then -- set on match
 t = { -- translate unspecified captures to y, m, and d
 [patterns[k][2]] = c1, -- fill the table of captures with the captures
 [patterns[k][3]] = c2, -- take index names from src\_pattern table and assign sequential captures
 [patterns[k][4]] = c3,
 };
 if month\_num[t.m] then -- when month not already a number
 t.m = month\_num[t.m]; -- replace valid month name with a number
 else
 return nil, 'iso'; -- not a valid date form because month not valid
 end

 return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format
 end
 end
 return nil, 'iso'; -- date could not be decoded; return nil and default iso date
end

--[[--------------------------< makeDate >-----------------------

Given year, month, day numbers, (zero-padded or not) return a full date in df format
where df may be one of:
 mdy, dmy, iso, ymd

on entry, year, month, day are presumed to be correct for the date that they represent; all are required

in this module, makeDate() is sometimes given an iso-format date in year:
 makeDate (2018-09-20, nil, nil, df)
this works because table.concat() sees only one table member

]]

local function makeDate (year, month, day, df)
 local format = {
 ['dmy'] = 'j F Y',
 ['mdy'] = 'F j, Y',
 ['ymd'] = 'Y F j',
 ['iso'] = 'Y-m-d',
 };

 local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here)

 if non\_western\_digits then -- this wiki uses non-western digits?
 date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 return mw.getContentLanguage():formatDate (format[df], date);
end

--[[--------------------------< I S \_ V A L I D \_ D A T E >----------------------------------------------------

Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth
be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies
Gregorian leapyear rules.

all arguments are required

]]

local function is\_valid\_date (year, month, day)
 local days\_in\_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 local month\_length;
 local y, m, d;
 local today = os.date ('\*t'); -- fetch a table of current date parts

 if not year or  *== year or not month or*  == month or not day or  *== day then*
 return false; -- something missing
 end

 y = tonumber (year);
 m = tonumber (month);
 d = tonumber (day);

 if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900?
 return false;
 end

 if (2==m) then -- if February
 month\_length = 28; -- then 28 days unless
 if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year?
 month\_length = 29; -- if leap year then 29 days in February
 end
 else
 month\_length=days\_in\_month[m];
 end

 if 1 > d or month\_length < d then -- day is within bounds
 return false;
 end
 -- here when date parts represent a valid date
 return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time
end

--[[--------------------------< decodeWebciteDate >-----------------------

Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format

returns date string in df format - webcite date is a unix timestamp encoded as bae62
or the string 'query'

]]

local function decodeWebciteDate(path, df)

 local dt = {};
 local decode;

 dt = mw.text.split(path, "/")

 -- valid URL formats that are not base62

 -- http://www.webcitation.org/query?id=1138911916587475
 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03
 -- http://www.webcitation.org/1138911916587475
 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e
 -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e

 if dt[2]:find ('query', 1, true) or
 dt[2]:find ('cache', 1, true) or
 dt[2]:find ('getfile', 1, true) or
 tonumber(dt[2]) then
 return 'query';
 end

 decode = base62(dt[2]); -- base62 string -> exponential number
 if not decode then
 return nil; -- nil return when dt[2] contains characters not in %w
 end
 dt = os.date('\*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts

 decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits
 if non\_western\_digits then -- this wiki uses non-western digits?
 decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 return decode;
end

--[[--------------------------< decodeWaybackDate >-----------------------

Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com )
or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com)
or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com)

return the formatted date eg. "September 1, 2016" in df format
Handle non-digits in snapshot ID such as "re\_" and "-" and "\*"

 valid date string in df format - wayback date is valid (including the text string 'index' when date is '/\*/')
 empty string - wayback date is malformed (less than 8 digits, not a valid date)
 nil - wayback date is '/save/' or otherwise not a number

 second return value is an appropriate 'message' may or may not be formatted

]]

local function decodeWaybackDate(path, df)

 local msg, snapdate;

 snapdate = path:gsub ('^/web/', *):gsub ('^/all/',* ):gsub ('^/ukgwa/', *):gsub ('^/tna/',* ):gsub ('^/', *); -- remove leading /web/, /all/, /ukgwa/, /tna/, or /*
 snapdate = snapdate:match ('^[^/]+'); -- get timestamp
 if snapdate == "\*" then -- eg. /web/\*/http.., etc.
 return 'index'; -- return indicator that this url has an index date
 end

 snapdate = snapdate:gsub ('%a%a\_%d?$', *):gsub ('%-',* ); -- from date, remove any trailing "re\_", dashes

 msg = *;*
 if snapdate:match ('%\*$') then -- a trailing '\*' causes calendar display at archive .org
 snapdate = snapdate:gsub ('%\*$', *); -- remove so not part of length calc later*
 msg = inlineRed (err\_warn\_msgs.ts\_cal, 'warning'); -- make a message
 end

 if not tonumber(snapdate) then
 return nil, 'ts\_nan'; -- return nil (fatal error flag) and message selector
 end

 local dlen = snapdate:len();
 if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return *, inlineRed (err\_warn\_msgs.ts\_short, 'error'); -- return empty string and error message*
 end

 local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here

 if not is\_valid\_date (year, month, day) then
 return *, inlineRed (err\_warn\_msgs.ts\_date, 'error'); -- return empty string and error message*
 end

 snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format
 if 14 == dlen then
 return snapdate, msg; -- return date with message if any
 else
 return snapdate, msg .. inlineRed (err\_warn\_msgs.ts\_len, 'warning'); -- return date with warning message(s)
 end
end

--[[--------------------------< decodeArchiveisDate >-----------------------

Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com)
return the date in df format (e.g. if df = dmy, return 28 August 2016)
Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552

 valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form)
 empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date)
 nil - wayback date is '/save/'

 second return value is an appropriate 'message' may or may not be formatted

]]

local function decodeArchiveisDate(path, df)
 local snapdate

 if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else
 return "short link" -- e.g. http://archive.is/hD1qz
 end

 snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', *); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens*

 local dlen = string.len(snapdate)
 if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return *, inlineRed (err\_warn\_msgs.ts\_short, 'error'); -- return empty string and error message*
 end

 local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here

 if not is\_valid\_date (year, month, day) then
 return *, inlineRed (err\_warn\_msgs.ts\_date, 'error'); -- return empty string and error message*
 end

 snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format
 if 14 == dlen then
 return snapdate; -- return date
 else
 return snapdate, inlineRed (err\_warn\_msgs.ts\_len, 'warning'); -- return date with warning message
 end
 end

--[[--------------------------< serviceName >-----------------------

Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID

]]

local function serviceName(host, no\_link)
 local tracking;
 local index;

 host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains

 if services[host] then
 index = host;
 else
 for k, \_ in pairs (services) do
 if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then
 index = k;
 break;
 end
 end
 end

 if index then
 local out = {*}; -- empty string in [1] so that concatenated result has leading single space*
 ulx.url1.service = services[index][4] or 'other';
 tracking = services[index][5] or categories.other;
 -- build tail string
 if false == services[index][1] then -- select prefix
 table.insert (out, prefixes.at);
 elseif true == services[index][1] then
 table.insert (out, prefixes.atthe);
 else
 table.insert (out, services[index][1]);
 end

 table.insert (out, make\_wikilink (services[index][2], services[index][3], no\_link)); -- add article wikilink
 if services[index][6] then -- add tail postfix if it exists
 table.insert (out, services[index][6]);
 end

 ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character

 else -- here when unknown archive
 ulx.url1.service = 'other';
 tracking = categories.unknown;
 ulx.url1.tail = table.concat ({*, prefixes.at, host, inlineRed (err\_warn\_msgs.unknown\_url, error)}, ' ');*
 end

 track[tracking] = 1
end

--[[--------------------------< parseExtraArgs >-----------------------

Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10
 For example: Template loop detected: Template:Webarchive
 Three url arguments not in numeric sequence (1..4..7).
 Function only processes arguments numbered 2 or greater (in this case 4 and 7)
 It creates numeric sequenced table entries like:
 urlx.url2.url = <argument value for url4>
 urlx.url3.url = <argument value for url7>
 Returns the number of URL arguments found numbered 2 or greater (in this case returns "2")

 ]]

local function parseExtraArgs(args)

 local i, j, argurl, argurl2, argdate, argtitle

 j = 2
 for i = 2, config.maxurls do
 argurl = "url" .. i
 if args[argurl] then
 argurl2 = "url" .. j
 ulx[argurl2] = {}
 ulx[argurl2]["url"] = args[argurl]
 argdate = "date" .. i
 if args[argdate] then
 ulx[argurl2]["date"] = args[argdate]
 else
 ulx[argurl2]["date"] = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 end

 argtitle = "title" .. i
 if args[argtitle] then
 ulx[argurl2]["title"] = args[argtitle]
 else
 ulx[argurl2]["title"] = nil
 end
 j = j + 1
 end
 end

 if j == 2 then
 return 0
 else
 return j - 2
 end
end

--[[--------------------------< comma >-----------------------

Given a date string, return "," if it's MDY

]]

local function comma(date)
 return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or *;*
end

--[[--------------------------< createRendering >-----------------------

Return a rendering of the data in ulx[][]

]]

local function createRendering()

 local displayfield
 local out = {};

 local index\_date, msg = ulx.url1.date:match ('(index)(.\*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message)
 ulx.url1.date = ulx.url1.date:gsub ('index.\*', 'index'); -- remove message

 if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx.url1.url); -- add url

 if ulx.url1.title then
 table.insert (out, ' ') -- the required space
 table.insert (out, ulx.url1.title) -- the title
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, ulx.url1.tail); -- tail text
 if ulx.url1.date then
 table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space?
 table.insert (out, 'index' == ulx.url1.date and s\_text.archive or s\_text.archived); -- add text
 table.insert (out, ' '); -- insert a space
 table.insert (out, ulx.url1.date); -- add date
 table.insert (out, ')'); -- close date text
 end
 else -- no title
 if index\_date then -- when url date is 'index'
 table.insert (out, table.concat ({' ', s\_text.Archive\_index, ']'})); -- add the index link label
 table.insert (out, msg or *); -- add date mismatch message when url date is /\*/ and |date= has valid date*
 else
 table.insert (out, table.concat ({' ', s\_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one)
 end
 if ulx.url1.date then
 if 'index' ~= ulx.url1.date then
 table.insert (out, ulx.url1.date); -- add date when data is not 'index'
 end
 table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy
 table.insert (out, ulx.url1.tail); -- add tail text
 else -- no date
 table.insert (out, ulx.url1.tail); -- add tail text
 end
 end

 if 0 < ulx.url1.extraurls then -- For multiple archive URLs
 local tot = ulx.url1.extraurls + 1
 table.insert (out, '.') -- terminate first url
 table.insert (out, table.concat ({' ', s\_text.addlarchives, ': '})); -- add header text

 for i=2, tot do -- loop through the additionals
 local index = table.concat ({'url', i}); -- make an index
 displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx[index]['url']); -- add the url
 table.insert (out, ' '); -- the required space
 table.insert (out, ulx[index][displayfield]); -- add the label
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, i==tot and '.' or ', '); -- add terminator
 end
 end
 return table.concat (out); -- make a big string and done

 else -- For Template:Cite archives
 if 'addlarchives' == ulx.url1.format then -- Multiple archive services
 table.insert (out, table.concat ({s\_text.addlarchives, ': '})); -- add header text
 else -- Multiple pages from the same archive
 table.insert (out, table.concat ({s\_text.addlpages, ' '})); -- add header text
 table.insert (out, ulx.url1.date); -- add date to header text
 table.insert (out, ': '); -- close header text
 end

 local tot = ulx.url1.extraurls + 1;
 for i=1, tot do -- loop through the additionals
 local index = table.concat ({'url', i}); -- make an index
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx[index]['url']); -- add url
 table.insert (out, ' '); -- add required space

 displayfield = ulx[index]['title'];
 if 'addlarchives' == ulx.url1.format then
 if not displayfield then
 displayfield = ulx[index]['date']
 end
 else -- must be addlpages
 if not displayfield then
 displayfield = table.concat ({s\_text.Page, ' ', i});
 end
 end
 table.insert (out, displayfield); -- add title, date, page label text
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, (i==tot and '.' or ', ')); -- add terminator
 end
 return table.concat (out); -- make a big string and done
 end
end

--[[--------------------------< P A R A M E T E R \_ N A M E \_ X L A T E >--------------------------------------

for internaltionalization, translate local-language parameter names to their English equivalents

TODO: return error message if multiple aliases of the same canonical parameter name are found?

returns two tables:
 new\_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form
 origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language

unrecognized parameters are ignored

]]

local function parameter\_name\_xlate (args, params, enum\_params)
 local name; -- holds modifiable name of the parameter name during evaluation
 local enum; -- for enumerated parameters, holds the enumerator during evaluation
 local found = false; -- flag used to break out of nested for loops
 local new\_args = {}; -- a table that holds canonical and translated parameter k/v pairs
 local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging
 local unnamed\_params; -- set true when unsupported positional parameters are detected

 for k, v in pairs (args) do -- loop through all of the arguments in the args table
 name = k; -- copy of original parameter name

 if 'string' == type (k) then
 if non\_western\_digits then -- true when non-western digits supported at this wiki
 name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else

 if not enum then -- no enumerator so looking for non-enumnerated parameters
 -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further
 for pname, aliases in pairs (params) do -- loop through each parameter the params table
 for \_, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
 if name == alias then
 new\_args[pname] = v; -- create a new entry in the new\_args table
 origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name
 found = true; -- flag so that we can break out of these nested for loops
 break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 if found then -- true when we found an alias that matched name
 found = false; -- reset the flag
 break; -- go do next args k/v pair
 end
 end
 else -- enumerated parameters
 name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search
 -- TODO: insert shortcut here? if num\_params[name] then name holds the canonical parameter name; no need to search further
 for pname, aliases in pairs (enum\_params) do -- loop through each parameter the num\_params table
 for \_, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
 if name == alias then
 pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator
 new\_args[pname] = v; -- create a new entry in the new\_args table
 origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name
 found = true; -- flag so that we can break out of these nested for loops
 break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 if found then -- true when we found an alias that matched name
 found = false; -- reset the flag
 break; -- go do next args k/v pair
 end
 end
 end
 else
 unnamed\_params = true; -- flag for unsupported positional parameters
 end
 end -- for k, v
 return new\_args, origin, unnamed\_params;
end

--[[--------------------------< W E B A R C H I V E >----------------------------------------------------------

template entry point

]]

local function webarchive(frame)
 local args = getArgs (frame);

 local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live
 'Module:Webarchive/data',
 frame:getTitle():find('sandbox', 1, true) and '/sandbox' or  *-- this instance is ./sandbox then append /sandbox*
 }));
 categories = data.categories; -- fill in the forward declarations
 config = data.config;
 if data.digits.enable then
 digits = data.digits; -- for i18n; table of digits in the local wiki's language
 non\_western\_digits = true; -- use\_non\_western\_digits
 end
 err\_warn\_msgs = data.err\_warn\_msgs;
 excepted\_pages = data.excepted\_pages;
 month\_num = data.month\_num; -- for i18n; table of month names in the local wiki's language
 prefixes = data.prefixes;
 services = data.services;
 s\_text = data.s\_text;
 uncategorized\_namespaces = data.uncategorized\_namespaces;
 uncategorized\_subpages = data.uncategorized\_subpages;

 local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used
 local unnamed\_params; -- boolean set to true when template call has unnamed parameters
 args, origin, unnamed\_params = parameter\_name\_xlate (args, data.params, data.enum\_params); -- translate parameter names in args to English

 local date, format, msg, udate, uri, url;
 local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format

 if args.url and args.url1 then -- URL argument (first)
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.url, origin.url1});
 end

 url = args.url or args.url1;

 if not url then
 return inlineError (data.crit\_err\_msgs.empty);
 end
 -- these iabot bugs perportedly fixed; removing these causes lua script error
--[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests
 if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 track[categories.error] = 1;
 return inlineError (data.crit\_err\_msgs.iabot1);
 end
 if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 track[categories.error] = 1;
 return inlineError (data.crit\_err\_msgs.iabot2);
 end
]]

 if not (url:lower():find ('^http') or url:find ('^//')) then
 return inlineError (data.crit\_err\_msgs.invalid\_url );
 end

 ulx.url1 = {}
 ulx.url1.url = url

 ulx.url1.extraurls = parseExtraArgs(args)

 local good = false;
 good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed

 if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed
 return inlineError (data.crit\_err\_msgs.invalid\_url);
 end

 serviceName(uri.host, args.nolink)

 if args.date and args.date1 then -- Date argument
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.date, origin.date1});
 end

 date = args.date or args.date1;
 date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space

 if date and config.verifydates then
 if '\*' == date then
 date = 'index';
 ldf = 'iso'; -- set to default format
 elseif 'mdy' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'mdy'; -- then |date=mdy overrides iso
 elseif 'dmy' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'dmy'; -- then |date=dmy overrides iso
 elseif 'ymd' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'ymd'; -- then |date=ymd overrides iso
 else
 date, ldf = decode\_date (date); -- get an iso format date from date and get date's original format
 end
 end

 if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is \*
 if not udate then -- this is the only 'fatal' error return
 return inlineError (data.crit\_err\_msgs[msg]);
 end

 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'), -- add warning message
 msg, -- add message if there is one
 });
 end
 end
 end
 else -- no |date=
 udate, msg = decodeWaybackDate (uri.path);

 if not udate then -- this is the only 'fatal' error return
 return inlineError (data.crit\_err\_msgs[msg]);
 end

 if  *== udate then*
 date = nil; -- unset
 else
 date = udate;
 end
 end

 elseif 'webcite' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate = decodeWebciteDate (uri.path); -- get the url date in iso format
 if 'query' ~= udate then -- skip if query
 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'),
 });
 end
 end
 end
 end
 else
 date = decodeWebciteDate( uri.path, "iso" )
 if date == "query" then
 date = nil; -- unset
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 elseif not date then -- invalid base62 string
 date = inlineRed (err\_warn\_msgs.date1, 'error');
 end
 end

 elseif 'archiveis' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format
 if 'short link' ~= udate then -- skip if short link
 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'), -- add warning message
 msg, -- add message if there is one
 });
 end
 end
 end
 end
 else -- no |date=
 udate, msg = decodeArchiveisDate( uri.path, "iso" )
 if udate == "short link" then
 date = nil; -- unset
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 elseif  *== udate then*
 date = nil; -- unset
 else
 date = udate;
 end
 end

 else -- some other service
 if not date then
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 end
 end

 if 'index' == date then
 ulx.url1.date = date .. (msg or *); -- create index + message (if there is one)*
 elseif date then
 ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or *); -- create a date in the wiki's local language + message (if there is one)*
 else
 ulx.url1.date = msg;
 end

 format = args.format; -- Format argument

 if not format then
 format = "none"
 else
 for k, v in pairs (data.format\_vals) do -- |format= accepts two specific values loop through a table of those values
 local found; -- declare a nil flag
 for \_, p in ipairs (v) do -- loop through local language variants
 if format == p then -- when |format= value matches
 format = k; -- use name from table key
 found = true; -- declare found so that we can break out of outer for loop
 break; -- break out of inner for loop
 end
 end

 if found then
 break;
 end
 end

 if format == "addlpages" then
 if not ulx.url1.date then
 format = "none"
 end
 elseif format == "addlarchives" then
 format = "addlarchives"
 else
 format = "none"
 end
 end
 ulx.url1.format = format

 if args.title and args.title1 then -- Title argument
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.title, origin.title1});
 end

 ulx.url1.title = args.title or args.title1;

 local rend = createRendering()
 if not rend then
 return inlineError (data.crit\_err\_msgs.unknown);
 end

 return rend .. ((unnamed\_params and inlineRed (err\_warn\_msgs.unnamed\_params, 'warning')) or *) .. createTracking();*

end

--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]

return {webarchive = webarchive};
63. ↑ Liu, Johnny. "Tomorrow Never Dies", *Game Revolution*, December 1999.
64. ↑ Perry, Doug. "Tomorrow Never Dies", *IGN*, 19 November 1999.

## External links

* Tomorrow Never Dies at Wikipedia

* *Tomorrow Never Dies* at James Bond Wiki
* *Tomorrow Never Dies* at the Internet Movie Database
* *Tomorrow Never Dies* at the TCM Movie Database
* *Tomorrow Never Dies* at Metro-Goldwyn-Mayer
* *Tomorrow Never Dies* at AFI Catalog